archastro.platform.v1.resources.solutions

   1# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License.
   2# This file is auto-generated by @archastro/sdk-generator. Do not edit.
   3# Content hash: 593cc7ef4728
   4
   5from __future__ import annotations
   6
   7import builtins
   8from typing import Any, Literal, Required, TypedDict
   9
  10from ...runtime.http_client import HttpClient, SyncHttpClient
  11from ...types.common import (
  12    ProductKnowledgeReconcileResponse,
  13    SolutionDependentsResponse,
  14    SolutionImportResponse,
  15    SolutionInstallResponse,
  16    SolutionListResponse,
  17    SolutionSummary,
  18    SolutionUpgradeResponse,
  19)
  20
  21
  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."
  29
  30
  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.'
  48
  49
  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"`.'
  57
  58
  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"`).'
  68
  69
  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`)."
  79
  80
  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."
  90
  91
  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"`).'
  99
 100
 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"`).'
 108
 109
 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`."
 127
 128
 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."
 146
 147
 148class SolutionInstallInputAclAddItem(TypedDict, total=False):
 149    actions: Required[list[str]]
 150    'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.'
 151    principal: str | None
 152    'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.'
 153    principal_type: Required[str]
 154    'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
 155
 156
 157class SolutionInstallInputAclGrantsItem(TypedDict, total=False):
 158    actions: Required[list[str]]
 159    'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.'
 160    principal: str | None
 161    'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.'
 162    principal_type: Required[str]
 163    'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
 164
 165
 166class SolutionInstallInputAclRemoveItem(TypedDict, total=False):
 167    principal: str | None
 168    'The identifier of the principal to remove. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`. Omit when `principal_type` is `"everyone"`.'
 169    principal_type: Required[str]
 170    'The kind of principal to remove. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
 171
 172
 173class SolutionInstallInputAcl(TypedDict, total=False):
 174    add: list[SolutionInstallInputAclAddItem] | None
 175    "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`."
 176    grants: list[SolutionInstallInputAclGrantsItem] | None
 177    "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`."
 178    remove: list[SolutionInstallInputAclRemoveItem] | None
 179    "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`."
 180
 181
 182class SolutionInstallInputDetailsPrefills(TypedDict, total=False):
 183    participants: dict[str, Any] | None
 184    "Participant slot-to-agent mappings applied by the platform. Caller values at these slots must match exactly."
 185    payload: dict[str, Any] | None
 186    "Partial invocation payload applied by the platform. A caller may omit these values, but supplying a different value at any locked path is rejected."
 187
 188
 189class SolutionInstallInputDetails(TypedDict, total=False):
 190    prefills: SolutionInstallInputDetailsPrefills | None
 191    "Instance-specific locked payload and participant values. Payload paths and participant slots are validated against the installed template's resolved input schema and workflow."
 192    type: Required[Literal["automation"]]
 193    "Install-details discriminator. Always `automation` for this variant."
 194
 195
 196class SolutionInstallInput(TypedDict, total=False):
 197    "Install a Solution"
 198
 199    acl: SolutionInstallInputAcl | None
 200    "Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team."
 201    allow_auto_import: bool | None
 202    "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."
 203    details: SolutionInstallInputDetails | None
 204    'Template-specific install options selected by the `type` discriminator. AutomationTemplate installs accept `{type: "automation", prefills: ...}`.'
 205    install_inputs: dict[str, Any] | None
 206    'Values applied to parameterized AgentTemplate prose during a root Agent install. Use `{values: {customer_label: "Acme"}}`.'
 207    lookup_key: str | None
 208    "Lookup key override for the provisioned resource (for example, the Agent's `agent_key`)."
 209    lookup_key_suffix: str | None
 210    "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."
 211    name: str | None
 212    "Display name override for a provisioned Agent or Automation. Ignored for attachment Solutions."
 213    org: str | None
 214    "Organization ID (`org_...`) for the install destination scope."
 215    target: str | None
 216    "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."
 217    team: str | None
 218    "Team ID (`team_...`) for the install destination scope."
 219    template: str | None
 220    "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."
 221    user: str | None
 222    "User ID (`usr_...`) for the install destination scope."
 223
 224
 225class SolutionUpgradeInputSolutionBundleConfigsItem(TypedDict, total=False):
 226    content: Required[str]
 227    "Full text content of the configuration file."
 228    content_type: str | None
 229    'MIME type of the configuration content, e.g. `"application/x-yaml"` or `"application/json"`. `null` if not specified.'
 230    relative_path: Required[str]
 231    "Bundle-relative path to this config file. The path determines the config kind and its storage identity within the installation."
 232
 233
 234class SolutionUpgradeInputSolutionBundleSetupActionsItem(TypedDict, total=False):
 235    depends_on: list[str] | None
 236    "List of other setup action identifiers that must be completed before this action becomes actionable."
 237    description: str | None
 238    "Markdown-formatted instructions or context shown beneath the checklist item. `null` if not provided."
 239    kind: Required[str]
 240    '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).'
 241    params: dict[str, Any] | None
 242    '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`.'
 243    required: bool | None
 244    "When `true`, this action must be completed before the checklist progress bar reaches 100%. Defaults to `true`."
 245    sort_order: int | None
 246    "Numeric sort position controlling the display order of this action in the checklist. Defaults to `0` when not specified."
 247    title: Required[str]
 248    "Short human-readable label displayed in the setup checklist."
 249    verify_config: dict[str, Any] | None
 250    '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.'
 251
 252
 253class SolutionUpgradeInputSolutionBundleSkillsItemFilesItem(TypedDict, total=False):
 254    content: Required[str]
 255    "Full text content of the file."
 256    content_type: str | None
 257    "MIME type of the file content. Defaults to a value inferred from the file extension when omitted."
 258    relative_path: Required[str]
 259    'Path of this file relative to the skill folder root, e.g. `"skills/my-skill/helpers.md"`.'
 260
 261
 262class SolutionUpgradeInputSolutionBundleSkillsItem(TypedDict, total=False):
 263    content: Required[str]
 264    "Full text content of the `SKILL.md` file."
 265    content_type: str | None
 266    "MIME type of the `SKILL.md` content. Defaults to `text/markdown` when omitted."
 267    files: list[SolutionUpgradeInputSolutionBundleSkillsItemFilesItem] | None
 268    "Additional files nested inside the skill folder, each with its own path and content."
 269    relative_path: Required[str]
 270    'Bundle-relative path to the skill root, which must end in `/SKILL.md` (e.g. `"skills/my-skill/SKILL.md"`).'
 271
 272
 273class SolutionUpgradeInputSolutionBundleSolutionFilesItem(TypedDict, total=False):
 274    content: Required[str]
 275    'Raw content of the file. When `data_encoding` is `"base64"`, this must be a valid base64-encoded string.'
 276    content_type: str | None
 277    "MIME type of the file. Defaults to a value inferred from the file extension when omitted."
 278    data_encoding: str | None
 279    '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.'
 280    relative_path: Required[str]
 281    "Path of this file relative to the solution root (e.g. `README.md`, `assets/diagram.png`)."
 282
 283
 284class SolutionUpgradeInputSolutionBundleSolution(TypedDict, total=False):
 285    content: Required[str]
 286    "Raw solution.yaml body (YAML or JSON). Describes the solution structure, template references, and asset declarations."
 287    content_type: str | None
 288    "MIME type of `content`. Defaults to `application/x-yaml`; pass `application/json` when submitting JSON."
 289    files: list[SolutionUpgradeInputSolutionBundleSolutionFilesItem] | None
 290    "Component files (READMEs, diagrams, fixtures) referenced by the solution.yaml via `path://` URIs. Each entry is persisted as a child file record."
 291    lookup_key: Required[str]
 292    "Stable lookup key for this solution. A suffix is appended at install time to namespace the stored config."
 293
 294
 295class SolutionUpgradeInputSolutionBundleTemplate(TypedDict, total=False):
 296    content: Required[str]
 297    "Full text content of the agent template file, typically a YAML document."
 298    content_type: str | None
 299    "MIME type of the template content. Defaults to `application/x-yaml` when omitted."
 300    relative_path: Required[str]
 301    'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).'
 302
 303
 304class SolutionUpgradeInputSolutionBundleTemplatesItem(TypedDict, total=False):
 305    content: Required[str]
 306    "Full text content of the agent template file, typically a YAML document."
 307    content_type: str | None
 308    "MIME type of the template content. Defaults to `application/x-yaml` when omitted."
 309    relative_path: Required[str]
 310    'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).'
 311
 312
 313class SolutionUpgradeInputSolutionBundle(TypedDict, total=False):
 314    configs: list[SolutionUpgradeInputSolutionBundleConfigsItem] | None
 315    "Additional configs of any kind that the solution.yaml references and that should be upserted as part of this install."
 316    lookup_key_prefix: str | None
 317    "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>`."
 318    lookup_key_suffix: str | None
 319    "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."
 320    setup_actions: list[SolutionUpgradeInputSolutionBundleSetupActionsItem] | None
 321    "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."
 322    skills: list[SolutionUpgradeInputSolutionBundleSkillsItem] | None
 323    "Skill bundles (root config plus supporting files) that this solution depends on."
 324    solution: Required[SolutionUpgradeInputSolutionBundleSolution]
 325    "The solution config to install, including the solution.yaml body and any referenced component files."
 326    template: SolutionUpgradeInputSolutionBundleTemplate | None
 327    "Convenience shorthand for supplying a single template. Equivalent to setting `templates: [template]`. Mutually exclusive with `templates`. Use `templates` directly when bundling multiple sibling templates."
 328    templates: list[SolutionUpgradeInputSolutionBundleTemplatesItem] | None
 329    "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`."
 330
 331
 332class SolutionUpgradeInput(TypedDict, total=False):
 333    "Upgrade an installed Solution"
 334
 335    allow_downgrade: bool | None
 336    "When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`."
 337    dry_run: bool | None
 338    "When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`."
 339    expected_review_fingerprint: str | None
 340    "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."
 341    org: str | None
 342    "Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this."
 343    solution_bundle: SolutionUpgradeInputSolutionBundle | None
 344    "Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with `target_solution`."
 345    target_solution: str | None
 346    "Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`."
 347
 348
 349class SolutionViewInput(TypedDict, total=False):
 350    "Track a Solution detail-page view"
 351
 352    anonymous: str | None
 353    "Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the `POST /api/v1/t` events use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session."
 354
 355
 356class AsyncSolutionResource:
 357    def __init__(self, http: HttpClient):
 358        self._http = http
 359
 360    async def list(
 361        self,
 362        *,
 363        page: int | None = None,
 364        page_size: int | None = None,
 365        lookup_key: str | None = None,
 366        path_prefix: str | None = None,
 367        owners: builtins.list[str] | None = None,
 368    ) -> SolutionListResponse:
 369        """
 370        List Solutions
 371        Returns a paginated list of Solutions visible to the caller, merging two
 372        scopes: app-level Solutions (system-owned rows with no org affiliation,
 373        visible to everyone including unauthenticated callers so they can power
 374        the public catalog) and org-level Solutions (system-owned rows stamped with
 375        the viewer's org ID, included when an authenticated viewer carries an org
 376        context). Unauthenticated callers resolve to an app-scoped anonymous viewer
 377        and therefore only ever see the app-level scope.
 378        Solutions that appear under both scopes are deduplicated by their stable
 379        `solution_id` value. The merged entry's `owners` array lists every scope the
 380        Solution was found under (`"system"` and/or `"org"`). When the app-level copy
 381        has a higher `solution_version` than the org-level copy, the response includes
 382        `upgrade_available: true` and `latest_version` so callers can prompt for an
 383        upgrade.
 384
 385        Args:
 386            page: Page number to return. Defaults to `1`.
 387            page_size: Number of Solutions per page. Defaults to `25`.
 388            lookup_key: Filter to the Solution whose `lookup_key` matches exactly.
 389            path_prefix: Filter to Solutions whose `virtual_path` starts with this prefix.
 390            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.
 391
 392        Returns:
 393            Paginated list of Solution summaries visible to the caller.
 394        """
 395        query: dict[str, object] = {}
 396        if page is not None:
 397            query["page"] = page
 398        if page_size is not None:
 399            query["page_size"] = page_size
 400        if lookup_key is not None:
 401            query["lookup_key"] = lookup_key
 402        if path_prefix is not None:
 403            query["path_prefix"] = path_prefix
 404        if owners is not None:
 405            query["owners"] = owners
 406        return await self._http.request(
 407            "/api/v1/solutions",
 408            query=query,
 409            response_type=SolutionListResponse,
 410        )
 411
 412    async def create(self, input: SolutionCreateInput) -> SolutionImportResponse:
 413        """
 414        Import a Solution into the library
 415        Imports a Solution and its bundled configs (skills, scripts, templates, files)
 416        into the library for the target scope. Two mutually exclusive import modes
 417        are supported: pass `solution` to re-import an existing system-owned catalog
 418        Solution by ID or `lookup_key`, or pass `solution_bundle` to supply a
 419        self-contained inline bundle. Exactly one must be present.
 420        The operation upserts the bundle in a single transaction. When `dry_run` is
 421        `true` the same pipeline runs but the transaction is rolled back no rows are
 422        persisted and the response reflects what would have been written. The
 423        response shape is the same in both cases: the Solution summary plus
 424        `installed_configs` listing each config the import created or would create.
 425        Pairs with `POST /api/v1/solutions/:solution/install`: this endpoint puts the
 426        Solution into the library; install provisions a runtime resource (Agent,
 427        AgentRoutine, AgentTool, etc.) from an already-imported Solution.
 428
 429        Args:
 430            input: Request body.
 431            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`.
 432            input.org: Organization ID (`org_...`) for the import destination scope.
 433            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`.
 434            input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`.
 435            input.team: Team ID (`team_...`) for the import destination scope.
 436            input.user: User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set.
 437            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.
 438
 439        Returns:
 440            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.
 441        """
 442        return await self._http.request(
 443            "/api/v1/solutions",
 444            method="POST",
 445            body=input,
 446            response_type=SolutionImportResponse,
 447        )
 448
 449    async def delete(self, solution: str) -> None:
 450        """
 451        Delete a Solution
 452        Permanently deletes an imported Solution and all configs bundled with it,
 453        including templates, skills, scripts, and files. The deletion runs in a
 454        single transaction; provider-stored blobs are swept asynchronously after commit.
 455        Org-scope callers (for example, an org admin in the Library settings page) can
 456        only delete their org's copy of the Solution. App-scope callers can delete
 457        Solutions at either scope. RBAC is enforced inside the core delete flow.
 458        Returns `204 No Content` on success.
 459
 460        Args:
 461            solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to delete.
 462
 463        Returns:
 464            Empty body. HTTP 204 indicates the Solution was deleted successfully.
 465        """
 466        await self._http.request(f"/api/v1/solutions/{solution}", method="DELETE")
 467
 468    async def get(self, solution: str) -> SolutionSummary:
 469        """
 470        Retrieve a Solution
 471        Returns a single Solution identified by its config ID (`cfg_...`) or
 472        `lookup_key`, in the same summary shape the list endpoint emits. The response
 473        always includes a freshly-minted `readme_url` call this endpoint to refresh
 474        an expired README token without making any other state change.
 475        Visibility matches the list endpoint: app-level Solutions (no org affiliation)
 476        are visible to everyone including unauthenticated callers, so the public
 477        catalog can render a Solution's detail page logged-out; org-scoped Solutions
 478        are only visible to authenticated viewers whose org context matches. Anything
 479        else returns 404.
 480        When the resolved Solution is org-scoped, the endpoint compares its
 481        `solution_version` against the matching app-level copy. If the app-level copy
 482        is at a higher version the response includes `upgrade_available: true` and
 483        `latest_version`. App-level resolutions always report `upgrade_available: false`.
 484
 485        Args:
 486            solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to retrieve.
 487
 488        Returns:
 489            Solution summary including a freshly-minted `readme_url` token valid for one hour.
 490        """
 491        return await self._http.request(
 492            f"/api/v1/solutions/{solution}",
 493            response_type=SolutionSummary,
 494        )
 495
 496    async def dependents(self, solution: str) -> SolutionDependentsResponse:
 497        """
 498        Preview Solution delete impact
 499        Returns a read-only preview of what deleting the specified Solution would
 500        affect: the agents that reference the Solution's bundle, and the count of
 501        bundled configs that would be orphaned rather than cascade-deleted.
 502        Use this endpoint before calling `DELETE /api/v1/solutions/:solution` to
 503        surface a warning when live agents depend on the Solution.
 504        Visibility scope mirrors the delete endpoint: org-scope viewers see their
 505        org's copy; app-scope viewers can inspect either scope.
 506
 507        Args:
 508            solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to inspect.
 509
 510        Returns:
 511            Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete.
 512        """
 513        return await self._http.request(
 514            f"/api/v1/solutions/{solution}/dependents",
 515            response_type=SolutionDependentsResponse,
 516        )
 517
 518    async def image(
 519        self, solution: str, token: str, *, v: str | None = None, file: str | None = None
 520    ) -> dict[str, str]:
 521        """
 522        Fetch a Solution cover image or gallery screenshot
 523        Returns the raw bytes of a Solution's cover image the bundled asset the
 524        Solution body's `image:` field names or, with the optional `file` param,
 525        one of its gallery screenshots (`screenshots:` entries). This endpoint backs
 526        the `image_url` and `screenshot_urls` fields of catalog payloads (such as
 527        `GET /api/v1/solutions`), which anonymous consumers the public
 528        marketplace's page cache, OpenGraph scrapers may hold far longer than a
 529        signed storage URL lives. Authorization is performed via a short, stable
 530        capability `token` rather than an HTTP header, so the URL never expires.
 531        The `token` is an HMAC-based capability tied to the Solution config ID. It
 532        does not expire, but the endpoint checks at fetch time that the Solution
 533        still declares the requested image without `file`, that it still declares
 534        a cover; with `file`, that the path is still among the body's declared
 535        `image`/`screenshots` so republishing without the asset (or hiding the
 536        Solution) turns the URL into a 404. Shared caches may continue serving the
 537        old image until the `Cache-Control` max-age of one hour elapses. `v` is an
 538        opaque cache key minted alongside the token; it changes when the Solution
 539        changes and is ignored by verification.
 540        All failure modes invalid config ID, invalid token, hidden Solution, no
 541        declared cover, a `file` outside the declared set, or an asset that doesn't
 542        resolve to a bundled image return a uniform `404` to avoid acting as an
 543        existence oracle.
 544
 545        Args:
 546            solution: Solution config ID (`cfg_...`) whose cover image to fetch.
 547            token: HMAC capability token authorizing access to this Solution's cover. Obtained from the `image_url` minted when the Solution was serialized.
 548            v: Opaque cache key minted alongside the token; changes when the Solution changes. Ignored by token verification.
 549            file: Bundled asset path of the image to serve (for example `images/setup.png`). Must be one of the paths the Solution body currently declares in `image`/`screenshots` anything else is a `404`. When absent the declared cover (`image:`) is served.
 550
 551        Returns:
 552            Raw cover image bytes, served with the asset's image content type.
 553        """
 554        query: dict[str, object] = {}
 555        query["token"] = token
 556        if v is not None:
 557            query["v"] = v
 558        if file is not None:
 559            query["file"] = file
 560        return await self._http.request_raw(f"/api/v1/solutions/{solution}/image", query=query)
 561
 562    async def install(self, solution: str, input: SolutionInstallInput) -> SolutionInstallResponse:
 563        """
 564        Install a Solution
 565        Provisions a runtime resource from an already-imported Solution. The type of
 566        resource created depends on the template the Solution wraps: an
 567        `AgentTemplate` produces an Agent, an `AutomationTemplate` produces an
 568        Automation, and attachment templates (`AgentRoutineTemplate`,
 569        `AgentToolTemplate`, `AgentSkillTemplate`, `AgentComputerTemplate`) attach a
 570        sub-resource to an existing Agent specified by `target`.
 571        For Solutions that bundle more than one template, pass `template` (the ID or
 572        `lookup_key` of the desired template) to select which one to provision.
 573        Single-template Solutions do not require `template`.
 574        Pairs with `POST /api/v1/solutions` (import): import puts the Solution into
 575        the library; install provisions a runtime resource from it.
 576
 577        Args:
 578            solution: Config ID (`cfg_...`) or `lookup_key` of the already-imported Solution to install from.
 579            input: Request body.
 580            input.acl: Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team.
 581            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.
 582            input.details: Template-specific install options selected by the `type` discriminator. AutomationTemplate installs accept `{type: "automation", prefills: ...}`.
 583            input.install_inputs: Values applied to parameterized AgentTemplate prose during a root Agent install. Use `{values: {customer_label: "Acme"}}`.
 584            input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's `agent_key`).
 585            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.
 586            input.name: Display name override for a provisioned Agent or Automation. Ignored for attachment Solutions.
 587            input.org: Organization ID (`org_...`) for the install destination scope.
 588            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.
 589            input.team: Team ID (`team_...`) for the install destination scope.
 590            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.
 591            input.user: User ID (`usr_...`) for the install destination scope.
 592
 593        Returns:
 594            The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID.
 595        """
 596        return await self._http.request(
 597            f"/api/v1/solutions/{solution}/install",
 598            method="POST",
 599            body=input,
 600            response_type=SolutionInstallResponse,
 601        )
 602
 603    async def readme(self, solution: str, token: str, *, file: str | None = None) -> dict[str, str]:
 604        """
 605        Retrieve a Solution README or asset
 606        Serves the README markdown or a bundled asset for an imported Solution. Both
 607        modes use the same path and require a short-lived signed `token` in the query
 608        string rather than an `Authorization` header, so browsers can load asset URLs
 609        directly from `<img src>` attributes without custom request logic.
 610        When `file` is omitted the response is the Solution's `readme` field rendered
 611        as `text/markdown`. All local asset references in the markdown are rewritten to
 612        point back at this endpoint with `?file=PATH&token=TOKEN` so browsers can load
 613        images inline without additional authentication.
 614        When `file` is set the response is the raw bytes of the matching asset (a File
 615        child whose `relative_path` equals `PATH`, or an inline `assets` entry by
 616        name) with the asset's stored `Content-Type`.
 617        Tokens are scoped to a single Solution, carry the viewer's app, org, and
 618        sandbox context from the time they were minted, and expire after one hour.
 619        Obtain a fresh token by calling `GET /api/v1/solutions/:solution`, which
 620        always returns a newly minted `readme_url`.
 621
 622        Args:
 623            solution: Solution config ID (`cfg_...`) identifying the Solution whose README or asset to retrieve.
 624            token: Signed URL token minted by the list or show endpoint. Expires after one hour.
 625            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.
 626
 627        Returns:
 628            README markdown (`text/markdown`) when `file` is omitted, or the raw asset bytes with the asset's `Content-Type` when `file` is set.
 629        """
 630        query: dict[str, object] = {}
 631        query["token"] = token
 632        if file is not None:
 633            query["file"] = file
 634        return await self._http.request_raw(f"/api/v1/solutions/{solution}/readme", query=query)
 635
 636    async def reconcile_product_knowledge(self, solution: str) -> ProductKnowledgeReconcileResponse:
 637        """
 638        Reconcile Customer Management product knowledge
 639        Recomputes every stamped customer's knowledge-search source allowlist
 640        from the template-resolved customer refs plus the install's current marked
 641        product-knowledge sources. Safe to retry; partial failures are returned by
 642        customer key rather than hidden behind an aggregate success.
 643
 644        Args:
 645            solution: Installed Customer Management Solution config (`cfg_...`) to reconcile.
 646
 647        Returns:
 648            Per-customer convergence result for this installed solution.
 649        """
 650        return await self._http.request(
 651            f"/api/v1/solutions/{solution}/reconcile_product_knowledge",
 652            method="POST",
 653            response_type=ProductKnowledgeReconcileResponse,
 654        )
 655
 656    async def upgrade(self, solution: str, input: SolutionUpgradeInput) -> SolutionUpgradeResponse:
 657        """
 658        Upgrade an installed Solution
 659        Applies an incoming bundle to an already-installed Solution in a single atomic
 660        transaction, bringing its configs in line with the new bundle. Config IDs are
 661        preserved across the upgrade. Configs that existed in the old bundle but are
 662        absent from the new one are orphaned (top-level) or hard-deleted (child rows).
 663        Two mutually exclusive source modes: pass `target_solution` to pull the
 664        incoming bundle from an existing Solution by ID or `lookup_key`, or pass
 665        `solution_bundle` to supply a complete inline bundle directly. Exactly one
 666        must be present.
 667        When `dry_run` is `true` the full diff is computed and returned but no
 668        changes are written. Pass the dry-run response's `review_fingerprint` as
 669        `expected_review_fingerprint` when applying to guard against the bundle
 670        changing between review and apply.
 671
 672        Args:
 673            solution: Config ID (`cfg_...`) or `lookup_key` of the currently installed Solution to upgrade.
 674            input: Request body.
 675            input.allow_downgrade: When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`.
 676            input.dry_run: When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`.
 677            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.
 678            input.org: Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this.
 679            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`.
 680            input.target_solution: Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`.
 681
 682        Returns:
 683            Updated Solution plus the full upgrade diff.
 684        """
 685        return await self._http.request(
 686            f"/api/v1/solutions/{solution}/upgrade",
 687            method="POST",
 688            body=input,
 689            response_type=SolutionUpgradeResponse,
 690        )
 691
 692    async def view(self, solution: str, input: SolutionViewInput) -> None:
 693        """
 694        Track a Solution detail-page view
 695        Records a `solution_viewed` analytics event for the identified Solution and
 696        returns `204 No Content`. Fired by the marketplace when a Solution's detail
 697        page is rendered in a browser, so publishers can see impressions alongside
 698        installs in their Solution analytics.
 699        Visibility matches `GET /api/v1/solutions/:solution`: unauthenticated callers
 700        (the logged-out marketplace) can only track Solutions published to the public
 701        catalog; anything the caller could not retrieve returns 404 and records
 702        nothing.
 703        Pass `anonymous` (the analytics visitor ID) so logged-out views can be
 704        counted as unique viewers. The event's Solution and publisher attribution are
 705        resolved server-side from the Solution row never from request input.
 706
 707        Args:
 708            solution: Solution config ID (`cfg_...`) or `lookup_key` of the viewed Solution.
 709            input: Request body.
 710            input.anonymous: Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the `POST /api/v1/t` events use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session.
 711
 712        Returns:
 713            Empty body. HTTP 204 indicates the Solution view was recorded successfully.
 714        """
 715        await self._http.request(f"/api/v1/solutions/{solution}/view", method="POST", body=input)
 716
 717
 718class SolutionResource:
 719    def __init__(self, http: SyncHttpClient):
 720        self._http = http
 721
 722    def list(
 723        self,
 724        *,
 725        page: int | None = None,
 726        page_size: int | None = None,
 727        lookup_key: str | None = None,
 728        path_prefix: str | None = None,
 729        owners: builtins.list[str] | None = None,
 730    ) -> SolutionListResponse:
 731        """
 732        List Solutions
 733        Returns a paginated list of Solutions visible to the caller, merging two
 734        scopes: app-level Solutions (system-owned rows with no org affiliation,
 735        visible to everyone including unauthenticated callers so they can power
 736        the public catalog) and org-level Solutions (system-owned rows stamped with
 737        the viewer's org ID, included when an authenticated viewer carries an org
 738        context). Unauthenticated callers resolve to an app-scoped anonymous viewer
 739        and therefore only ever see the app-level scope.
 740        Solutions that appear under both scopes are deduplicated by their stable
 741        `solution_id` value. The merged entry's `owners` array lists every scope the
 742        Solution was found under (`"system"` and/or `"org"`). When the app-level copy
 743        has a higher `solution_version` than the org-level copy, the response includes
 744        `upgrade_available: true` and `latest_version` so callers can prompt for an
 745        upgrade.
 746
 747        Args:
 748            page: Page number to return. Defaults to `1`.
 749            page_size: Number of Solutions per page. Defaults to `25`.
 750            lookup_key: Filter to the Solution whose `lookup_key` matches exactly.
 751            path_prefix: Filter to Solutions whose `virtual_path` starts with this prefix.
 752            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.
 753
 754        Returns:
 755            Paginated list of Solution summaries visible to the caller.
 756        """
 757        query: dict[str, object] = {}
 758        if page is not None:
 759            query["page"] = page
 760        if page_size is not None:
 761            query["page_size"] = page_size
 762        if lookup_key is not None:
 763            query["lookup_key"] = lookup_key
 764        if path_prefix is not None:
 765            query["path_prefix"] = path_prefix
 766        if owners is not None:
 767            query["owners"] = owners
 768        return self._http.request(
 769            "/api/v1/solutions",
 770            query=query,
 771            response_type=SolutionListResponse,
 772        )
 773
 774    def create(self, input: SolutionCreateInput) -> SolutionImportResponse:
 775        """
 776        Import a Solution into the library
 777        Imports a Solution and its bundled configs (skills, scripts, templates, files)
 778        into the library for the target scope. Two mutually exclusive import modes
 779        are supported: pass `solution` to re-import an existing system-owned catalog
 780        Solution by ID or `lookup_key`, or pass `solution_bundle` to supply a
 781        self-contained inline bundle. Exactly one must be present.
 782        The operation upserts the bundle in a single transaction. When `dry_run` is
 783        `true` the same pipeline runs but the transaction is rolled back no rows are
 784        persisted and the response reflects what would have been written. The
 785        response shape is the same in both cases: the Solution summary plus
 786        `installed_configs` listing each config the import created or would create.
 787        Pairs with `POST /api/v1/solutions/:solution/install`: this endpoint puts the
 788        Solution into the library; install provisions a runtime resource (Agent,
 789        AgentRoutine, AgentTool, etc.) from an already-imported Solution.
 790
 791        Args:
 792            input: Request body.
 793            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`.
 794            input.org: Organization ID (`org_...`) for the import destination scope.
 795            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`.
 796            input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`.
 797            input.team: Team ID (`team_...`) for the import destination scope.
 798            input.user: User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set.
 799            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.
 800
 801        Returns:
 802            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.
 803        """
 804        return self._http.request(
 805            "/api/v1/solutions",
 806            method="POST",
 807            body=input,
 808            response_type=SolutionImportResponse,
 809        )
 810
 811    def delete(self, solution: str) -> None:
 812        """
 813        Delete a Solution
 814        Permanently deletes an imported Solution and all configs bundled with it,
 815        including templates, skills, scripts, and files. The deletion runs in a
 816        single transaction; provider-stored blobs are swept asynchronously after commit.
 817        Org-scope callers (for example, an org admin in the Library settings page) can
 818        only delete their org's copy of the Solution. App-scope callers can delete
 819        Solutions at either scope. RBAC is enforced inside the core delete flow.
 820        Returns `204 No Content` on success.
 821
 822        Args:
 823            solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to delete.
 824
 825        Returns:
 826            Empty body. HTTP 204 indicates the Solution was deleted successfully.
 827        """
 828        self._http.request(f"/api/v1/solutions/{solution}", method="DELETE")
 829
 830    def get(self, solution: str) -> SolutionSummary:
 831        """
 832        Retrieve a Solution
 833        Returns a single Solution identified by its config ID (`cfg_...`) or
 834        `lookup_key`, in the same summary shape the list endpoint emits. The response
 835        always includes a freshly-minted `readme_url` call this endpoint to refresh
 836        an expired README token without making any other state change.
 837        Visibility matches the list endpoint: app-level Solutions (no org affiliation)
 838        are visible to everyone including unauthenticated callers, so the public
 839        catalog can render a Solution's detail page logged-out; org-scoped Solutions
 840        are only visible to authenticated viewers whose org context matches. Anything
 841        else returns 404.
 842        When the resolved Solution is org-scoped, the endpoint compares its
 843        `solution_version` against the matching app-level copy. If the app-level copy
 844        is at a higher version the response includes `upgrade_available: true` and
 845        `latest_version`. App-level resolutions always report `upgrade_available: false`.
 846
 847        Args:
 848            solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to retrieve.
 849
 850        Returns:
 851            Solution summary including a freshly-minted `readme_url` token valid for one hour.
 852        """
 853        return self._http.request(f"/api/v1/solutions/{solution}", response_type=SolutionSummary)
 854
 855    def dependents(self, solution: str) -> SolutionDependentsResponse:
 856        """
 857        Preview Solution delete impact
 858        Returns a read-only preview of what deleting the specified Solution would
 859        affect: the agents that reference the Solution's bundle, and the count of
 860        bundled configs that would be orphaned rather than cascade-deleted.
 861        Use this endpoint before calling `DELETE /api/v1/solutions/:solution` to
 862        surface a warning when live agents depend on the Solution.
 863        Visibility scope mirrors the delete endpoint: org-scope viewers see their
 864        org's copy; app-scope viewers can inspect either scope.
 865
 866        Args:
 867            solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to inspect.
 868
 869        Returns:
 870            Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete.
 871        """
 872        return self._http.request(
 873            f"/api/v1/solutions/{solution}/dependents",
 874            response_type=SolutionDependentsResponse,
 875        )
 876
 877    def image(
 878        self, solution: str, token: str, *, v: str | None = None, file: str | None = None
 879    ) -> dict[str, str]:
 880        """
 881        Fetch a Solution cover image or gallery screenshot
 882        Returns the raw bytes of a Solution's cover image the bundled asset the
 883        Solution body's `image:` field names or, with the optional `file` param,
 884        one of its gallery screenshots (`screenshots:` entries). This endpoint backs
 885        the `image_url` and `screenshot_urls` fields of catalog payloads (such as
 886        `GET /api/v1/solutions`), which anonymous consumers the public
 887        marketplace's page cache, OpenGraph scrapers may hold far longer than a
 888        signed storage URL lives. Authorization is performed via a short, stable
 889        capability `token` rather than an HTTP header, so the URL never expires.
 890        The `token` is an HMAC-based capability tied to the Solution config ID. It
 891        does not expire, but the endpoint checks at fetch time that the Solution
 892        still declares the requested image without `file`, that it still declares
 893        a cover; with `file`, that the path is still among the body's declared
 894        `image`/`screenshots` so republishing without the asset (or hiding the
 895        Solution) turns the URL into a 404. Shared caches may continue serving the
 896        old image until the `Cache-Control` max-age of one hour elapses. `v` is an
 897        opaque cache key minted alongside the token; it changes when the Solution
 898        changes and is ignored by verification.
 899        All failure modes invalid config ID, invalid token, hidden Solution, no
 900        declared cover, a `file` outside the declared set, or an asset that doesn't
 901        resolve to a bundled image return a uniform `404` to avoid acting as an
 902        existence oracle.
 903
 904        Args:
 905            solution: Solution config ID (`cfg_...`) whose cover image to fetch.
 906            token: HMAC capability token authorizing access to this Solution's cover. Obtained from the `image_url` minted when the Solution was serialized.
 907            v: Opaque cache key minted alongside the token; changes when the Solution changes. Ignored by token verification.
 908            file: Bundled asset path of the image to serve (for example `images/setup.png`). Must be one of the paths the Solution body currently declares in `image`/`screenshots` anything else is a `404`. When absent the declared cover (`image:`) is served.
 909
 910        Returns:
 911            Raw cover image bytes, served with the asset's image content type.
 912        """
 913        query: dict[str, object] = {}
 914        query["token"] = token
 915        if v is not None:
 916            query["v"] = v
 917        if file is not None:
 918            query["file"] = file
 919        return self._http.request_raw(f"/api/v1/solutions/{solution}/image", query=query)
 920
 921    def install(self, solution: str, input: SolutionInstallInput) -> SolutionInstallResponse:
 922        """
 923        Install a Solution
 924        Provisions a runtime resource from an already-imported Solution. The type of
 925        resource created depends on the template the Solution wraps: an
 926        `AgentTemplate` produces an Agent, an `AutomationTemplate` produces an
 927        Automation, and attachment templates (`AgentRoutineTemplate`,
 928        `AgentToolTemplate`, `AgentSkillTemplate`, `AgentComputerTemplate`) attach a
 929        sub-resource to an existing Agent specified by `target`.
 930        For Solutions that bundle more than one template, pass `template` (the ID or
 931        `lookup_key` of the desired template) to select which one to provision.
 932        Single-template Solutions do not require `template`.
 933        Pairs with `POST /api/v1/solutions` (import): import puts the Solution into
 934        the library; install provisions a runtime resource from it.
 935
 936        Args:
 937            solution: Config ID (`cfg_...`) or `lookup_key` of the already-imported Solution to install from.
 938            input: Request body.
 939            input.acl: Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team.
 940            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.
 941            input.details: Template-specific install options selected by the `type` discriminator. AutomationTemplate installs accept `{type: "automation", prefills: ...}`.
 942            input.install_inputs: Values applied to parameterized AgentTemplate prose during a root Agent install. Use `{values: {customer_label: "Acme"}}`.
 943            input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's `agent_key`).
 944            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.
 945            input.name: Display name override for a provisioned Agent or Automation. Ignored for attachment Solutions.
 946            input.org: Organization ID (`org_...`) for the install destination scope.
 947            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.
 948            input.team: Team ID (`team_...`) for the install destination scope.
 949            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.
 950            input.user: User ID (`usr_...`) for the install destination scope.
 951
 952        Returns:
 953            The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID.
 954        """
 955        return self._http.request(
 956            f"/api/v1/solutions/{solution}/install",
 957            method="POST",
 958            body=input,
 959            response_type=SolutionInstallResponse,
 960        )
 961
 962    def readme(self, solution: str, token: str, *, file: str | None = None) -> dict[str, str]:
 963        """
 964        Retrieve a Solution README or asset
 965        Serves the README markdown or a bundled asset for an imported Solution. Both
 966        modes use the same path and require a short-lived signed `token` in the query
 967        string rather than an `Authorization` header, so browsers can load asset URLs
 968        directly from `<img src>` attributes without custom request logic.
 969        When `file` is omitted the response is the Solution's `readme` field rendered
 970        as `text/markdown`. All local asset references in the markdown are rewritten to
 971        point back at this endpoint with `?file=PATH&token=TOKEN` so browsers can load
 972        images inline without additional authentication.
 973        When `file` is set the response is the raw bytes of the matching asset (a File
 974        child whose `relative_path` equals `PATH`, or an inline `assets` entry by
 975        name) with the asset's stored `Content-Type`.
 976        Tokens are scoped to a single Solution, carry the viewer's app, org, and
 977        sandbox context from the time they were minted, and expire after one hour.
 978        Obtain a fresh token by calling `GET /api/v1/solutions/:solution`, which
 979        always returns a newly minted `readme_url`.
 980
 981        Args:
 982            solution: Solution config ID (`cfg_...`) identifying the Solution whose README or asset to retrieve.
 983            token: Signed URL token minted by the list or show endpoint. Expires after one hour.
 984            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.
 985
 986        Returns:
 987            README markdown (`text/markdown`) when `file` is omitted, or the raw asset bytes with the asset's `Content-Type` when `file` is set.
 988        """
 989        query: dict[str, object] = {}
 990        query["token"] = token
 991        if file is not None:
 992            query["file"] = file
 993        return self._http.request_raw(f"/api/v1/solutions/{solution}/readme", query=query)
 994
 995    def reconcile_product_knowledge(self, solution: str) -> ProductKnowledgeReconcileResponse:
 996        """
 997        Reconcile Customer Management product knowledge
 998        Recomputes every stamped customer's knowledge-search source allowlist
 999        from the template-resolved customer refs plus the install's current marked
1000        product-knowledge sources. Safe to retry; partial failures are returned by
1001        customer key rather than hidden behind an aggregate success.
1002
1003        Args:
1004            solution: Installed Customer Management Solution config (`cfg_...`) to reconcile.
1005
1006        Returns:
1007            Per-customer convergence result for this installed solution.
1008        """
1009        return self._http.request(
1010            f"/api/v1/solutions/{solution}/reconcile_product_knowledge",
1011            method="POST",
1012            response_type=ProductKnowledgeReconcileResponse,
1013        )
1014
1015    def upgrade(self, solution: str, input: SolutionUpgradeInput) -> SolutionUpgradeResponse:
1016        """
1017        Upgrade an installed Solution
1018        Applies an incoming bundle to an already-installed Solution in a single atomic
1019        transaction, bringing its configs in line with the new bundle. Config IDs are
1020        preserved across the upgrade. Configs that existed in the old bundle but are
1021        absent from the new one are orphaned (top-level) or hard-deleted (child rows).
1022        Two mutually exclusive source modes: pass `target_solution` to pull the
1023        incoming bundle from an existing Solution by ID or `lookup_key`, or pass
1024        `solution_bundle` to supply a complete inline bundle directly. Exactly one
1025        must be present.
1026        When `dry_run` is `true` the full diff is computed and returned but no
1027        changes are written. Pass the dry-run response's `review_fingerprint` as
1028        `expected_review_fingerprint` when applying to guard against the bundle
1029        changing between review and apply.
1030
1031        Args:
1032            solution: Config ID (`cfg_...`) or `lookup_key` of the currently installed Solution to upgrade.
1033            input: Request body.
1034            input.allow_downgrade: When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`.
1035            input.dry_run: When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`.
1036            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.
1037            input.org: Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this.
1038            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`.
1039            input.target_solution: Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`.
1040
1041        Returns:
1042            Updated Solution plus the full upgrade diff.
1043        """
1044        return self._http.request(
1045            f"/api/v1/solutions/{solution}/upgrade",
1046            method="POST",
1047            body=input,
1048            response_type=SolutionUpgradeResponse,
1049        )
1050
1051    def view(self, solution: str, input: SolutionViewInput) -> None:
1052        """
1053        Track a Solution detail-page view
1054        Records a `solution_viewed` analytics event for the identified Solution and
1055        returns `204 No Content`. Fired by the marketplace when a Solution's detail
1056        page is rendered in a browser, so publishers can see impressions alongside
1057        installs in their Solution analytics.
1058        Visibility matches `GET /api/v1/solutions/:solution`: unauthenticated callers
1059        (the logged-out marketplace) can only track Solutions published to the public
1060        catalog; anything the caller could not retrieve returns 404 and records
1061        nothing.
1062        Pass `anonymous` (the analytics visitor ID) so logged-out views can be
1063        counted as unique viewers. The event's Solution and publisher attribution are
1064        resolved server-side from the Solution row never from request input.
1065
1066        Args:
1067            solution: Solution config ID (`cfg_...`) or `lookup_key` of the viewed Solution.
1068            input: Request body.
1069            input.anonymous: Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the `POST /api/v1/t` events use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session.
1070
1071        Returns:
1072            Empty body. HTTP 204 indicates the Solution view was recorded successfully.
1073        """
1074        self._http.request(f"/api/v1/solutions/{solution}/view", method="POST", body=input)
class SolutionCreateInputSolutionBundleConfigsItem(typing.TypedDict):
23class SolutionCreateInputSolutionBundleConfigsItem(TypedDict, total=False):
24    content: Required[str]
25    "Full text content of the configuration file."
26    content_type: str | None
27    'MIME type of the configuration content, e.g. `"application/x-yaml"` or `"application/json"`. `null` if not specified.'
28    relative_path: Required[str]
29    "Bundle-relative path to this config file. The path determines the config kind and its storage identity within the installation."
content: Required[str]

Full text content of the configuration file.

content_type: str | None

MIME type of the configuration content, e.g. "application/x-yaml" or "application/json". null if not specified.

relative_path: Required[str]

Bundle-relative path to this config file. The path determines the config kind and its storage identity within the installation.

class SolutionCreateInputSolutionBundleSetupActionsItem(typing.TypedDict):
32class SolutionCreateInputSolutionBundleSetupActionsItem(TypedDict, total=False):
33    depends_on: list[str] | None
34    "List of other setup action identifiers that must be completed before this action becomes actionable."
35    description: str | None
36    "Markdown-formatted instructions or context shown beneath the checklist item. `null` if not provided."
37    kind: Required[str]
38    '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).'
39    params: dict[str, Any] | None
40    '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`.'
41    required: bool | None
42    "When `true`, this action must be completed before the checklist progress bar reaches 100%. Defaults to `true`."
43    sort_order: int | None
44    "Numeric sort position controlling the display order of this action in the checklist. Defaults to `0` when not specified."
45    title: Required[str]
46    "Short human-readable label displayed in the setup checklist."
47    verify_config: dict[str, Any] | None
48    '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.'
depends_on: list[str] | None

List of other setup action identifiers that must be completed before this action becomes actionable.

description: str | None

Markdown-formatted instructions or context shown beneath the checklist item. null if not provided.

kind: Required[str]

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).

params: dict[str, typing.Any] | None

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.

required: bool | None

When true, this action must be completed before the checklist progress bar reaches 100%. Defaults to true.

sort_order: int | None

Numeric sort position controlling the display order of this action in the checklist. Defaults to 0 when not specified.

title: Required[str]

Short human-readable label displayed in the setup checklist.

verify_config: dict[str, typing.Any] | None

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.

class SolutionCreateInputSolutionBundleSkillsItemFilesItem(typing.TypedDict):
51class SolutionCreateInputSolutionBundleSkillsItemFilesItem(TypedDict, total=False):
52    content: Required[str]
53    "Full text content of the file."
54    content_type: str | None
55    "MIME type of the file content. Defaults to a value inferred from the file extension when omitted."
56    relative_path: Required[str]
57    'Path of this file relative to the skill folder root, e.g. `"skills/my-skill/helpers.md"`.'
content: Required[str]

Full text content of the file.

content_type: str | None

MIME type of the file content. Defaults to a value inferred from the file extension when omitted.

relative_path: Required[str]

Path of this file relative to the skill folder root, e.g. "skills/my-skill/helpers.md".

class SolutionCreateInputSolutionBundleSkillsItem(typing.TypedDict):
60class SolutionCreateInputSolutionBundleSkillsItem(TypedDict, total=False):
61    content: Required[str]
62    "Full text content of the `SKILL.md` file."
63    content_type: str | None
64    "MIME type of the `SKILL.md` content. Defaults to `text/markdown` when omitted."
65    files: list[SolutionCreateInputSolutionBundleSkillsItemFilesItem] | None
66    "Additional files nested inside the skill folder, each with its own path and content."
67    relative_path: Required[str]
68    'Bundle-relative path to the skill root, which must end in `/SKILL.md` (e.g. `"skills/my-skill/SKILL.md"`).'
content: Required[str]

Full text content of the SKILL.md file.

content_type: str | None

MIME type of the SKILL.md content. Defaults to text/markdown when omitted.

Additional files nested inside the skill folder, each with its own path and content.

relative_path: Required[str]

Bundle-relative path to the skill root, which must end in /SKILL.md (e.g. "skills/my-skill/SKILL.md").

class SolutionCreateInputSolutionBundleSolutionFilesItem(typing.TypedDict):
71class SolutionCreateInputSolutionBundleSolutionFilesItem(TypedDict, total=False):
72    content: Required[str]
73    'Raw content of the file. When `data_encoding` is `"base64"`, this must be a valid base64-encoded string.'
74    content_type: str | None
75    "MIME type of the file. Defaults to a value inferred from the file extension when omitted."
76    data_encoding: str | None
77    '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.'
78    relative_path: Required[str]
79    "Path of this file relative to the solution root (e.g. `README.md`, `assets/diagram.png`)."
content: Required[str]

Raw content of the file. When data_encoding is "base64", this must be a valid base64-encoded string.

content_type: str | None

MIME type of the file. Defaults to a value inferred from the file extension when omitted.

data_encoding: str | None

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.

relative_path: Required[str]

Path of this file relative to the solution root (e.g. README.md, assets/diagram.png).

class SolutionCreateInputSolutionBundleSolution(typing.TypedDict):
82class SolutionCreateInputSolutionBundleSolution(TypedDict, total=False):
83    content: Required[str]
84    "Raw solution.yaml body (YAML or JSON). Describes the solution structure, template references, and asset declarations."
85    content_type: str | None
86    "MIME type of `content`. Defaults to `application/x-yaml`; pass `application/json` when submitting JSON."
87    files: list[SolutionCreateInputSolutionBundleSolutionFilesItem] | None
88    "Component files (READMEs, diagrams, fixtures) referenced by the solution.yaml via `path://` URIs. Each entry is persisted as a child file record."
89    lookup_key: Required[str]
90    "Stable lookup key for this solution. A suffix is appended at install time to namespace the stored config."
content: Required[str]

Raw solution.yaml body (YAML or JSON). Describes the solution structure, template references, and asset declarations.

content_type: str | None

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.

lookup_key: Required[str]

Stable lookup key for this solution. A suffix is appended at install time to namespace the stored config.

class SolutionCreateInputSolutionBundleTemplate(typing.TypedDict):
93class SolutionCreateInputSolutionBundleTemplate(TypedDict, total=False):
94    content: Required[str]
95    "Full text content of the agent template file, typically a YAML document."
96    content_type: str | None
97    "MIME type of the template content. Defaults to `application/x-yaml` when omitted."
98    relative_path: Required[str]
99    'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).'
content: Required[str]

Full text content of the agent template file, typically a YAML document.

content_type: str | None

MIME type of the template content. Defaults to application/x-yaml when omitted.

relative_path: Required[str]

Bundle-relative path to the template file, used to derive its storage identity (e.g. "agent.yaml").

class SolutionCreateInputSolutionBundleTemplatesItem(typing.TypedDict):
102class SolutionCreateInputSolutionBundleTemplatesItem(TypedDict, total=False):
103    content: Required[str]
104    "Full text content of the agent template file, typically a YAML document."
105    content_type: str | None
106    "MIME type of the template content. Defaults to `application/x-yaml` when omitted."
107    relative_path: Required[str]
108    'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).'
content: Required[str]

Full text content of the agent template file, typically a YAML document.

content_type: str | None

MIME type of the template content. Defaults to application/x-yaml when omitted.

relative_path: Required[str]

Bundle-relative path to the template file, used to derive its storage identity (e.g. "agent.yaml").

class SolutionCreateInputSolutionBundle(typing.TypedDict):
111class SolutionCreateInputSolutionBundle(TypedDict, total=False):
112    configs: list[SolutionCreateInputSolutionBundleConfigsItem] | None
113    "Additional configs of any kind that the solution.yaml references and that should be upserted as part of this install."
114    lookup_key_prefix: str | None
115    "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>`."
116    lookup_key_suffix: str | None
117    "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."
118    setup_actions: list[SolutionCreateInputSolutionBundleSetupActionsItem] | None
119    "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."
120    skills: list[SolutionCreateInputSolutionBundleSkillsItem] | None
121    "Skill bundles (root config plus supporting files) that this solution depends on."
122    solution: Required[SolutionCreateInputSolutionBundleSolution]
123    "The solution config to install, including the solution.yaml body and any referenced component files."
124    template: SolutionCreateInputSolutionBundleTemplate | None
125    "Convenience shorthand for supplying a single template. Equivalent to setting `templates: [template]`. Mutually exclusive with `templates`. Use `templates` directly when bundling multiple sibling templates."
126    templates: list[SolutionCreateInputSolutionBundleTemplatesItem] | None
127    "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.

lookup_key_prefix: str | None

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>.

lookup_key_suffix: str | None

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.

Convenience shorthand for supplying a single template. Equivalent to setting templates: [template]. Mutually exclusive with templates. Use templates directly when bundling multiple sibling templates.

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.

class SolutionCreateInput(typing.TypedDict):
130class SolutionCreateInput(TypedDict, total=False):
131    "Import a Solution into the library"
132
133    dry_run: bool | None
134    "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`."
135    org: str | None
136    "Organization ID (`org_...`) for the import destination scope."
137    solution: str | None
138    "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`."
139    solution_bundle: SolutionCreateInputSolutionBundle | None
140    "Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`."
141    team: str | None
142    "Team ID (`team_...`) for the import destination scope."
143    user: str | None
144    "User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set."
145    virtual_path_prefix: str | None
146    "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

dry_run: bool | None

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.

org: str | None

Organization ID (org_...) for the import destination scope.

solution: str | None

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.

solution_bundle: SolutionCreateInputSolutionBundle | None

Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with solution.

team: str | None

Team ID (team_...) for the import destination scope.

user: str | None

User ID (usr_...) for the import destination scope. Only one of org, team, or user may be set.

virtual_path_prefix: str | None

Path prefix under which all uploaded configs' virtual_path values are anchored (for example solutions/<uuid>). Stable per install; omit to use no prefix.

class SolutionInstallInputAclAddItem(typing.TypedDict):
149class SolutionInstallInputAclAddItem(TypedDict, total=False):
150    actions: Required[list[str]]
151    'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.'
152    principal: str | None
153    'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.'
154    principal_type: Required[str]
155    'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
actions: Required[list[str]]

Array of action strings the principal is permitted to perform, e.g. ["read", "write"]. Must contain at least one entry.

principal: str | None

The identifier of the principal. A string ID for "user", "team", "org", and "agent" types; one of "admin", "member", or "viewer" for "org_role"; omit entirely when principal_type is "everyone".

principal_type: Required[str]

The kind of principal receiving the grant. One of "user", "team", "org", "org_role", "agent", or "everyone".

class SolutionInstallInputAclGrantsItem(typing.TypedDict):
158class SolutionInstallInputAclGrantsItem(TypedDict, total=False):
159    actions: Required[list[str]]
160    'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.'
161    principal: str | None
162    'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.'
163    principal_type: Required[str]
164    'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
actions: Required[list[str]]

Array of action strings the principal is permitted to perform, e.g. ["read", "write"]. Must contain at least one entry.

principal: str | None

The identifier of the principal. A string ID for "user", "team", "org", and "agent" types; one of "admin", "member", or "viewer" for "org_role"; omit entirely when principal_type is "everyone".

principal_type: Required[str]

The kind of principal receiving the grant. One of "user", "team", "org", "org_role", "agent", or "everyone".

class SolutionInstallInputAclRemoveItem(typing.TypedDict):
167class SolutionInstallInputAclRemoveItem(TypedDict, total=False):
168    principal: str | None
169    'The identifier of the principal to remove. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`. Omit when `principal_type` is `"everyone"`.'
170    principal_type: Required[str]
171    'The kind of principal to remove. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
principal: str | None

The identifier of the principal to remove. A string ID for "user", "team", "org", and "agent" types; one of "admin", "member", or "viewer" for "org_role". Omit when principal_type is "everyone".

principal_type: Required[str]

The kind of principal to remove. One of "user", "team", "org", "org_role", "agent", or "everyone".

class SolutionInstallInputAcl(typing.TypedDict):
174class SolutionInstallInputAcl(TypedDict, total=False):
175    add: list[SolutionInstallInputAclAddItem] | None
176    "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`."
177    grants: list[SolutionInstallInputAclGrantsItem] | None
178    "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`."
179    remove: list[SolutionInstallInputAclRemoveItem] | None
180    "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`."
add: list[SolutionInstallInputAclAddItem] | None

Patch mode: grants to add or merge into the existing list. Cannot be combined with grants.

grants: list[SolutionInstallInputAclGrantsItem] | None

Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array ([]) to clear all grants. Cannot be combined with add or remove.

remove: list[SolutionInstallInputAclRemoveItem] | None

Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with grants.

class SolutionInstallInputDetailsPrefills(typing.TypedDict):
183class SolutionInstallInputDetailsPrefills(TypedDict, total=False):
184    participants: dict[str, Any] | None
185    "Participant slot-to-agent mappings applied by the platform. Caller values at these slots must match exactly."
186    payload: dict[str, Any] | None
187    "Partial invocation payload applied by the platform. A caller may omit these values, but supplying a different value at any locked path is rejected."
participants: dict[str, typing.Any] | None

Participant slot-to-agent mappings applied by the platform. Caller values at these slots must match exactly.

payload: dict[str, typing.Any] | None

Partial invocation payload applied by the platform. A caller may omit these values, but supplying a different value at any locked path is rejected.

class SolutionInstallInputDetails(typing.TypedDict):
190class SolutionInstallInputDetails(TypedDict, total=False):
191    prefills: SolutionInstallInputDetailsPrefills | None
192    "Instance-specific locked payload and participant values. Payload paths and participant slots are validated against the installed template's resolved input schema and workflow."
193    type: Required[Literal["automation"]]
194    "Install-details discriminator. Always `automation` for this variant."

Instance-specific locked payload and participant values. Payload paths and participant slots are validated against the installed template's resolved input schema and workflow.

type: Required[Literal['automation']]

Install-details discriminator. Always automation for this variant.

class SolutionInstallInput(typing.TypedDict):
197class SolutionInstallInput(TypedDict, total=False):
198    "Install a Solution"
199
200    acl: SolutionInstallInputAcl | None
201    "Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team."
202    allow_auto_import: bool | None
203    "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."
204    details: SolutionInstallInputDetails | None
205    'Template-specific install options selected by the `type` discriminator. AutomationTemplate installs accept `{type: "automation", prefills: ...}`.'
206    install_inputs: dict[str, Any] | None
207    'Values applied to parameterized AgentTemplate prose during a root Agent install. Use `{values: {customer_label: "Acme"}}`.'
208    lookup_key: str | None
209    "Lookup key override for the provisioned resource (for example, the Agent's `agent_key`)."
210    lookup_key_suffix: str | None
211    "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."
212    name: str | None
213    "Display name override for a provisioned Agent or Automation. Ignored for attachment Solutions."
214    org: str | None
215    "Organization ID (`org_...`) for the install destination scope."
216    target: str | None
217    "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."
218    team: str | None
219    "Team ID (`team_...`) for the install destination scope."
220    template: str | None
221    "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."
222    user: str | None
223    "User ID (`usr_...`) for the install destination scope."

Install a Solution

Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team.

allow_auto_import: bool | None

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.

details: SolutionInstallInputDetails | None

Template-specific install options selected by the type discriminator. AutomationTemplate installs accept {type: "automation", prefills: ...}.

install_inputs: dict[str, typing.Any] | None

Values applied to parameterized AgentTemplate prose during a root Agent install. Use {values: {customer_label: "Acme"}}.

lookup_key: str | None

Lookup key override for the provisioned resource (for example, the Agent's agent_key).

lookup_key_suffix: str | None

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.

name: str | None

Display name override for a provisioned Agent or Automation. Ignored for attachment Solutions.

org: str | None

Organization ID (org_...) for the install destination scope.

target: str | None

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.

team: str | None

Team ID (team_...) for the install destination scope.

template: str | None

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.

user: str | None

User ID (usr_...) for the install destination scope.

class SolutionUpgradeInputSolutionBundleConfigsItem(typing.TypedDict):
226class SolutionUpgradeInputSolutionBundleConfigsItem(TypedDict, total=False):
227    content: Required[str]
228    "Full text content of the configuration file."
229    content_type: str | None
230    'MIME type of the configuration content, e.g. `"application/x-yaml"` or `"application/json"`. `null` if not specified.'
231    relative_path: Required[str]
232    "Bundle-relative path to this config file. The path determines the config kind and its storage identity within the installation."
content: Required[str]

Full text content of the configuration file.

content_type: str | None

MIME type of the configuration content, e.g. "application/x-yaml" or "application/json". null if not specified.

relative_path: Required[str]

Bundle-relative path to this config file. The path determines the config kind and its storage identity within the installation.

class SolutionUpgradeInputSolutionBundleSetupActionsItem(typing.TypedDict):
235class SolutionUpgradeInputSolutionBundleSetupActionsItem(TypedDict, total=False):
236    depends_on: list[str] | None
237    "List of other setup action identifiers that must be completed before this action becomes actionable."
238    description: str | None
239    "Markdown-formatted instructions or context shown beneath the checklist item. `null` if not provided."
240    kind: Required[str]
241    '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).'
242    params: dict[str, Any] | None
243    '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`.'
244    required: bool | None
245    "When `true`, this action must be completed before the checklist progress bar reaches 100%. Defaults to `true`."
246    sort_order: int | None
247    "Numeric sort position controlling the display order of this action in the checklist. Defaults to `0` when not specified."
248    title: Required[str]
249    "Short human-readable label displayed in the setup checklist."
250    verify_config: dict[str, Any] | None
251    '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.'
depends_on: list[str] | None

List of other setup action identifiers that must be completed before this action becomes actionable.

description: str | None

Markdown-formatted instructions or context shown beneath the checklist item. null if not provided.

kind: Required[str]

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).

params: dict[str, typing.Any] | None

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.

required: bool | None

When true, this action must be completed before the checklist progress bar reaches 100%. Defaults to true.

sort_order: int | None

Numeric sort position controlling the display order of this action in the checklist. Defaults to 0 when not specified.

title: Required[str]

Short human-readable label displayed in the setup checklist.

verify_config: dict[str, typing.Any] | None

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.

class SolutionUpgradeInputSolutionBundleSkillsItemFilesItem(typing.TypedDict):
254class SolutionUpgradeInputSolutionBundleSkillsItemFilesItem(TypedDict, total=False):
255    content: Required[str]
256    "Full text content of the file."
257    content_type: str | None
258    "MIME type of the file content. Defaults to a value inferred from the file extension when omitted."
259    relative_path: Required[str]
260    'Path of this file relative to the skill folder root, e.g. `"skills/my-skill/helpers.md"`.'
content: Required[str]

Full text content of the file.

content_type: str | None

MIME type of the file content. Defaults to a value inferred from the file extension when omitted.

relative_path: Required[str]

Path of this file relative to the skill folder root, e.g. "skills/my-skill/helpers.md".

class SolutionUpgradeInputSolutionBundleSkillsItem(typing.TypedDict):
263class SolutionUpgradeInputSolutionBundleSkillsItem(TypedDict, total=False):
264    content: Required[str]
265    "Full text content of the `SKILL.md` file."
266    content_type: str | None
267    "MIME type of the `SKILL.md` content. Defaults to `text/markdown` when omitted."
268    files: list[SolutionUpgradeInputSolutionBundleSkillsItemFilesItem] | None
269    "Additional files nested inside the skill folder, each with its own path and content."
270    relative_path: Required[str]
271    'Bundle-relative path to the skill root, which must end in `/SKILL.md` (e.g. `"skills/my-skill/SKILL.md"`).'
content: Required[str]

Full text content of the SKILL.md file.

content_type: str | None

MIME type of the SKILL.md content. Defaults to text/markdown when omitted.

Additional files nested inside the skill folder, each with its own path and content.

relative_path: Required[str]

Bundle-relative path to the skill root, which must end in /SKILL.md (e.g. "skills/my-skill/SKILL.md").

class SolutionUpgradeInputSolutionBundleSolutionFilesItem(typing.TypedDict):
274class SolutionUpgradeInputSolutionBundleSolutionFilesItem(TypedDict, total=False):
275    content: Required[str]
276    'Raw content of the file. When `data_encoding` is `"base64"`, this must be a valid base64-encoded string.'
277    content_type: str | None
278    "MIME type of the file. Defaults to a value inferred from the file extension when omitted."
279    data_encoding: str | None
280    '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.'
281    relative_path: Required[str]
282    "Path of this file relative to the solution root (e.g. `README.md`, `assets/diagram.png`)."
content: Required[str]

Raw content of the file. When data_encoding is "base64", this must be a valid base64-encoded string.

content_type: str | None

MIME type of the file. Defaults to a value inferred from the file extension when omitted.

data_encoding: str | None

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.

relative_path: Required[str]

Path of this file relative to the solution root (e.g. README.md, assets/diagram.png).

class SolutionUpgradeInputSolutionBundleSolution(typing.TypedDict):
285class SolutionUpgradeInputSolutionBundleSolution(TypedDict, total=False):
286    content: Required[str]
287    "Raw solution.yaml body (YAML or JSON). Describes the solution structure, template references, and asset declarations."
288    content_type: str | None
289    "MIME type of `content`. Defaults to `application/x-yaml`; pass `application/json` when submitting JSON."
290    files: list[SolutionUpgradeInputSolutionBundleSolutionFilesItem] | None
291    "Component files (READMEs, diagrams, fixtures) referenced by the solution.yaml via `path://` URIs. Each entry is persisted as a child file record."
292    lookup_key: Required[str]
293    "Stable lookup key for this solution. A suffix is appended at install time to namespace the stored config."
content: Required[str]

Raw solution.yaml body (YAML or JSON). Describes the solution structure, template references, and asset declarations.

content_type: str | None

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.

lookup_key: Required[str]

Stable lookup key for this solution. A suffix is appended at install time to namespace the stored config.

class SolutionUpgradeInputSolutionBundleTemplate(typing.TypedDict):
296class SolutionUpgradeInputSolutionBundleTemplate(TypedDict, total=False):
297    content: Required[str]
298    "Full text content of the agent template file, typically a YAML document."
299    content_type: str | None
300    "MIME type of the template content. Defaults to `application/x-yaml` when omitted."
301    relative_path: Required[str]
302    'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).'
content: Required[str]

Full text content of the agent template file, typically a YAML document.

content_type: str | None

MIME type of the template content. Defaults to application/x-yaml when omitted.

relative_path: Required[str]

Bundle-relative path to the template file, used to derive its storage identity (e.g. "agent.yaml").

class SolutionUpgradeInputSolutionBundleTemplatesItem(typing.TypedDict):
305class SolutionUpgradeInputSolutionBundleTemplatesItem(TypedDict, total=False):
306    content: Required[str]
307    "Full text content of the agent template file, typically a YAML document."
308    content_type: str | None
309    "MIME type of the template content. Defaults to `application/x-yaml` when omitted."
310    relative_path: Required[str]
311    'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).'
content: Required[str]

Full text content of the agent template file, typically a YAML document.

content_type: str | None

MIME type of the template content. Defaults to application/x-yaml when omitted.

relative_path: Required[str]

Bundle-relative path to the template file, used to derive its storage identity (e.g. "agent.yaml").

class SolutionUpgradeInputSolutionBundle(typing.TypedDict):
314class SolutionUpgradeInputSolutionBundle(TypedDict, total=False):
315    configs: list[SolutionUpgradeInputSolutionBundleConfigsItem] | None
316    "Additional configs of any kind that the solution.yaml references and that should be upserted as part of this install."
317    lookup_key_prefix: str | None
318    "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>`."
319    lookup_key_suffix: str | None
320    "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."
321    setup_actions: list[SolutionUpgradeInputSolutionBundleSetupActionsItem] | None
322    "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."
323    skills: list[SolutionUpgradeInputSolutionBundleSkillsItem] | None
324    "Skill bundles (root config plus supporting files) that this solution depends on."
325    solution: Required[SolutionUpgradeInputSolutionBundleSolution]
326    "The solution config to install, including the solution.yaml body and any referenced component files."
327    template: SolutionUpgradeInputSolutionBundleTemplate | None
328    "Convenience shorthand for supplying a single template. Equivalent to setting `templates: [template]`. Mutually exclusive with `templates`. Use `templates` directly when bundling multiple sibling templates."
329    templates: list[SolutionUpgradeInputSolutionBundleTemplatesItem] | None
330    "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.

lookup_key_prefix: str | None

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>.

lookup_key_suffix: str | None

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.

Convenience shorthand for supplying a single template. Equivalent to setting templates: [template]. Mutually exclusive with templates. Use templates directly when bundling multiple sibling templates.

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.

class SolutionUpgradeInput(typing.TypedDict):
333class SolutionUpgradeInput(TypedDict, total=False):
334    "Upgrade an installed Solution"
335
336    allow_downgrade: bool | None
337    "When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`."
338    dry_run: bool | None
339    "When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`."
340    expected_review_fingerprint: str | None
341    "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."
342    org: str | None
343    "Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this."
344    solution_bundle: SolutionUpgradeInputSolutionBundle | None
345    "Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with `target_solution`."
346    target_solution: str | None
347    "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

allow_downgrade: bool | None

When true, permits an incoming solution_version lower than the currently installed version. Defaults to false.

dry_run: bool | None

When true, computes and returns the full upgrade diff without persisting any changes. Defaults to false.

expected_review_fingerprint: str | None

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.

org: str | None

Organization ID (org_...) used to resolve org-scoped lookup_key values. Config IDs (cfg_...) are globally unique and do not require this.

solution_bundle: SolutionUpgradeInputSolutionBundle | None

Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with target_solution.

target_solution: str | None

Config ID (cfg_...) or lookup_key of the Solution to use as the incoming upgrade source. Mutually exclusive with solution_bundle.

class SolutionViewInput(typing.TypedDict):
350class SolutionViewInput(TypedDict, total=False):
351    "Track a Solution detail-page view"
352
353    anonymous: str | None
354    "Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the `POST /api/v1/t` events use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session."

Track a Solution detail-page view

anonymous: str | None

Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the POST /api/v1/t events use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session.

class AsyncSolutionResource:
357class AsyncSolutionResource:
358    def __init__(self, http: HttpClient):
359        self._http = http
360
361    async def list(
362        self,
363        *,
364        page: int | None = None,
365        page_size: int | None = None,
366        lookup_key: str | None = None,
367        path_prefix: str | None = None,
368        owners: builtins.list[str] | None = None,
369    ) -> SolutionListResponse:
370        """
371        List Solutions
372        Returns a paginated list of Solutions visible to the caller, merging two
373        scopes: app-level Solutions (system-owned rows with no org affiliation,
374        visible to everyone including unauthenticated callers so they can power
375        the public catalog) and org-level Solutions (system-owned rows stamped with
376        the viewer's org ID, included when an authenticated viewer carries an org
377        context). Unauthenticated callers resolve to an app-scoped anonymous viewer
378        and therefore only ever see the app-level scope.
379        Solutions that appear under both scopes are deduplicated by their stable
380        `solution_id` value. The merged entry's `owners` array lists every scope the
381        Solution was found under (`"system"` and/or `"org"`). When the app-level copy
382        has a higher `solution_version` than the org-level copy, the response includes
383        `upgrade_available: true` and `latest_version` so callers can prompt for an
384        upgrade.
385
386        Args:
387            page: Page number to return. Defaults to `1`.
388            page_size: Number of Solutions per page. Defaults to `25`.
389            lookup_key: Filter to the Solution whose `lookup_key` matches exactly.
390            path_prefix: Filter to Solutions whose `virtual_path` starts with this prefix.
391            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.
392
393        Returns:
394            Paginated list of Solution summaries visible to the caller.
395        """
396        query: dict[str, object] = {}
397        if page is not None:
398            query["page"] = page
399        if page_size is not None:
400            query["page_size"] = page_size
401        if lookup_key is not None:
402            query["lookup_key"] = lookup_key
403        if path_prefix is not None:
404            query["path_prefix"] = path_prefix
405        if owners is not None:
406            query["owners"] = owners
407        return await self._http.request(
408            "/api/v1/solutions",
409            query=query,
410            response_type=SolutionListResponse,
411        )
412
413    async def create(self, input: SolutionCreateInput) -> SolutionImportResponse:
414        """
415        Import a Solution into the library
416        Imports a Solution and its bundled configs (skills, scripts, templates, files)
417        into the library for the target scope. Two mutually exclusive import modes
418        are supported: pass `solution` to re-import an existing system-owned catalog
419        Solution by ID or `lookup_key`, or pass `solution_bundle` to supply a
420        self-contained inline bundle. Exactly one must be present.
421        The operation upserts the bundle in a single transaction. When `dry_run` is
422        `true` the same pipeline runs but the transaction is rolled back no rows are
423        persisted and the response reflects what would have been written. The
424        response shape is the same in both cases: the Solution summary plus
425        `installed_configs` listing each config the import created or would create.
426        Pairs with `POST /api/v1/solutions/:solution/install`: this endpoint puts the
427        Solution into the library; install provisions a runtime resource (Agent,
428        AgentRoutine, AgentTool, etc.) from an already-imported Solution.
429
430        Args:
431            input: Request body.
432            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`.
433            input.org: Organization ID (`org_...`) for the import destination scope.
434            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`.
435            input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`.
436            input.team: Team ID (`team_...`) for the import destination scope.
437            input.user: User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set.
438            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.
439
440        Returns:
441            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.
442        """
443        return await self._http.request(
444            "/api/v1/solutions",
445            method="POST",
446            body=input,
447            response_type=SolutionImportResponse,
448        )
449
450    async def delete(self, solution: str) -> None:
451        """
452        Delete a Solution
453        Permanently deletes an imported Solution and all configs bundled with it,
454        including templates, skills, scripts, and files. The deletion runs in a
455        single transaction; provider-stored blobs are swept asynchronously after commit.
456        Org-scope callers (for example, an org admin in the Library settings page) can
457        only delete their org's copy of the Solution. App-scope callers can delete
458        Solutions at either scope. RBAC is enforced inside the core delete flow.
459        Returns `204 No Content` on success.
460
461        Args:
462            solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to delete.
463
464        Returns:
465            Empty body. HTTP 204 indicates the Solution was deleted successfully.
466        """
467        await self._http.request(f"/api/v1/solutions/{solution}", method="DELETE")
468
469    async def get(self, solution: str) -> SolutionSummary:
470        """
471        Retrieve a Solution
472        Returns a single Solution identified by its config ID (`cfg_...`) or
473        `lookup_key`, in the same summary shape the list endpoint emits. The response
474        always includes a freshly-minted `readme_url` call this endpoint to refresh
475        an expired README token without making any other state change.
476        Visibility matches the list endpoint: app-level Solutions (no org affiliation)
477        are visible to everyone including unauthenticated callers, so the public
478        catalog can render a Solution's detail page logged-out; org-scoped Solutions
479        are only visible to authenticated viewers whose org context matches. Anything
480        else returns 404.
481        When the resolved Solution is org-scoped, the endpoint compares its
482        `solution_version` against the matching app-level copy. If the app-level copy
483        is at a higher version the response includes `upgrade_available: true` and
484        `latest_version`. App-level resolutions always report `upgrade_available: false`.
485
486        Args:
487            solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to retrieve.
488
489        Returns:
490            Solution summary including a freshly-minted `readme_url` token valid for one hour.
491        """
492        return await self._http.request(
493            f"/api/v1/solutions/{solution}",
494            response_type=SolutionSummary,
495        )
496
497    async def dependents(self, solution: str) -> SolutionDependentsResponse:
498        """
499        Preview Solution delete impact
500        Returns a read-only preview of what deleting the specified Solution would
501        affect: the agents that reference the Solution's bundle, and the count of
502        bundled configs that would be orphaned rather than cascade-deleted.
503        Use this endpoint before calling `DELETE /api/v1/solutions/:solution` to
504        surface a warning when live agents depend on the Solution.
505        Visibility scope mirrors the delete endpoint: org-scope viewers see their
506        org's copy; app-scope viewers can inspect either scope.
507
508        Args:
509            solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to inspect.
510
511        Returns:
512            Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete.
513        """
514        return await self._http.request(
515            f"/api/v1/solutions/{solution}/dependents",
516            response_type=SolutionDependentsResponse,
517        )
518
519    async def image(
520        self, solution: str, token: str, *, v: str | None = None, file: str | None = None
521    ) -> dict[str, str]:
522        """
523        Fetch a Solution cover image or gallery screenshot
524        Returns the raw bytes of a Solution's cover image the bundled asset the
525        Solution body's `image:` field names or, with the optional `file` param,
526        one of its gallery screenshots (`screenshots:` entries). This endpoint backs
527        the `image_url` and `screenshot_urls` fields of catalog payloads (such as
528        `GET /api/v1/solutions`), which anonymous consumers the public
529        marketplace's page cache, OpenGraph scrapers may hold far longer than a
530        signed storage URL lives. Authorization is performed via a short, stable
531        capability `token` rather than an HTTP header, so the URL never expires.
532        The `token` is an HMAC-based capability tied to the Solution config ID. It
533        does not expire, but the endpoint checks at fetch time that the Solution
534        still declares the requested image without `file`, that it still declares
535        a cover; with `file`, that the path is still among the body's declared
536        `image`/`screenshots` so republishing without the asset (or hiding the
537        Solution) turns the URL into a 404. Shared caches may continue serving the
538        old image until the `Cache-Control` max-age of one hour elapses. `v` is an
539        opaque cache key minted alongside the token; it changes when the Solution
540        changes and is ignored by verification.
541        All failure modes invalid config ID, invalid token, hidden Solution, no
542        declared cover, a `file` outside the declared set, or an asset that doesn't
543        resolve to a bundled image return a uniform `404` to avoid acting as an
544        existence oracle.
545
546        Args:
547            solution: Solution config ID (`cfg_...`) whose cover image to fetch.
548            token: HMAC capability token authorizing access to this Solution's cover. Obtained from the `image_url` minted when the Solution was serialized.
549            v: Opaque cache key minted alongside the token; changes when the Solution changes. Ignored by token verification.
550            file: Bundled asset path of the image to serve (for example `images/setup.png`). Must be one of the paths the Solution body currently declares in `image`/`screenshots` anything else is a `404`. When absent the declared cover (`image:`) is served.
551
552        Returns:
553            Raw cover image bytes, served with the asset's image content type.
554        """
555        query: dict[str, object] = {}
556        query["token"] = token
557        if v is not None:
558            query["v"] = v
559        if file is not None:
560            query["file"] = file
561        return await self._http.request_raw(f"/api/v1/solutions/{solution}/image", query=query)
562
563    async def install(self, solution: str, input: SolutionInstallInput) -> SolutionInstallResponse:
564        """
565        Install a Solution
566        Provisions a runtime resource from an already-imported Solution. The type of
567        resource created depends on the template the Solution wraps: an
568        `AgentTemplate` produces an Agent, an `AutomationTemplate` produces an
569        Automation, and attachment templates (`AgentRoutineTemplate`,
570        `AgentToolTemplate`, `AgentSkillTemplate`, `AgentComputerTemplate`) attach a
571        sub-resource to an existing Agent specified by `target`.
572        For Solutions that bundle more than one template, pass `template` (the ID or
573        `lookup_key` of the desired template) to select which one to provision.
574        Single-template Solutions do not require `template`.
575        Pairs with `POST /api/v1/solutions` (import): import puts the Solution into
576        the library; install provisions a runtime resource from it.
577
578        Args:
579            solution: Config ID (`cfg_...`) or `lookup_key` of the already-imported Solution to install from.
580            input: Request body.
581            input.acl: Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team.
582            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.
583            input.details: Template-specific install options selected by the `type` discriminator. AutomationTemplate installs accept `{type: "automation", prefills: ...}`.
584            input.install_inputs: Values applied to parameterized AgentTemplate prose during a root Agent install. Use `{values: {customer_label: "Acme"}}`.
585            input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's `agent_key`).
586            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.
587            input.name: Display name override for a provisioned Agent or Automation. Ignored for attachment Solutions.
588            input.org: Organization ID (`org_...`) for the install destination scope.
589            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.
590            input.team: Team ID (`team_...`) for the install destination scope.
591            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.
592            input.user: User ID (`usr_...`) for the install destination scope.
593
594        Returns:
595            The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID.
596        """
597        return await self._http.request(
598            f"/api/v1/solutions/{solution}/install",
599            method="POST",
600            body=input,
601            response_type=SolutionInstallResponse,
602        )
603
604    async def readme(self, solution: str, token: str, *, file: str | None = None) -> dict[str, str]:
605        """
606        Retrieve a Solution README or asset
607        Serves the README markdown or a bundled asset for an imported Solution. Both
608        modes use the same path and require a short-lived signed `token` in the query
609        string rather than an `Authorization` header, so browsers can load asset URLs
610        directly from `<img src>` attributes without custom request logic.
611        When `file` is omitted the response is the Solution's `readme` field rendered
612        as `text/markdown`. All local asset references in the markdown are rewritten to
613        point back at this endpoint with `?file=PATH&token=TOKEN` so browsers can load
614        images inline without additional authentication.
615        When `file` is set the response is the raw bytes of the matching asset (a File
616        child whose `relative_path` equals `PATH`, or an inline `assets` entry by
617        name) with the asset's stored `Content-Type`.
618        Tokens are scoped to a single Solution, carry the viewer's app, org, and
619        sandbox context from the time they were minted, and expire after one hour.
620        Obtain a fresh token by calling `GET /api/v1/solutions/:solution`, which
621        always returns a newly minted `readme_url`.
622
623        Args:
624            solution: Solution config ID (`cfg_...`) identifying the Solution whose README or asset to retrieve.
625            token: Signed URL token minted by the list or show endpoint. Expires after one hour.
626            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.
627
628        Returns:
629            README markdown (`text/markdown`) when `file` is omitted, or the raw asset bytes with the asset's `Content-Type` when `file` is set.
630        """
631        query: dict[str, object] = {}
632        query["token"] = token
633        if file is not None:
634            query["file"] = file
635        return await self._http.request_raw(f"/api/v1/solutions/{solution}/readme", query=query)
636
637    async def reconcile_product_knowledge(self, solution: str) -> ProductKnowledgeReconcileResponse:
638        """
639        Reconcile Customer Management product knowledge
640        Recomputes every stamped customer's knowledge-search source allowlist
641        from the template-resolved customer refs plus the install's current marked
642        product-knowledge sources. Safe to retry; partial failures are returned by
643        customer key rather than hidden behind an aggregate success.
644
645        Args:
646            solution: Installed Customer Management Solution config (`cfg_...`) to reconcile.
647
648        Returns:
649            Per-customer convergence result for this installed solution.
650        """
651        return await self._http.request(
652            f"/api/v1/solutions/{solution}/reconcile_product_knowledge",
653            method="POST",
654            response_type=ProductKnowledgeReconcileResponse,
655        )
656
657    async def upgrade(self, solution: str, input: SolutionUpgradeInput) -> SolutionUpgradeResponse:
658        """
659        Upgrade an installed Solution
660        Applies an incoming bundle to an already-installed Solution in a single atomic
661        transaction, bringing its configs in line with the new bundle. Config IDs are
662        preserved across the upgrade. Configs that existed in the old bundle but are
663        absent from the new one are orphaned (top-level) or hard-deleted (child rows).
664        Two mutually exclusive source modes: pass `target_solution` to pull the
665        incoming bundle from an existing Solution by ID or `lookup_key`, or pass
666        `solution_bundle` to supply a complete inline bundle directly. Exactly one
667        must be present.
668        When `dry_run` is `true` the full diff is computed and returned but no
669        changes are written. Pass the dry-run response's `review_fingerprint` as
670        `expected_review_fingerprint` when applying to guard against the bundle
671        changing between review and apply.
672
673        Args:
674            solution: Config ID (`cfg_...`) or `lookup_key` of the currently installed Solution to upgrade.
675            input: Request body.
676            input.allow_downgrade: When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`.
677            input.dry_run: When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`.
678            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.
679            input.org: Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this.
680            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`.
681            input.target_solution: Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`.
682
683        Returns:
684            Updated Solution plus the full upgrade diff.
685        """
686        return await self._http.request(
687            f"/api/v1/solutions/{solution}/upgrade",
688            method="POST",
689            body=input,
690            response_type=SolutionUpgradeResponse,
691        )
692
693    async def view(self, solution: str, input: SolutionViewInput) -> None:
694        """
695        Track a Solution detail-page view
696        Records a `solution_viewed` analytics event for the identified Solution and
697        returns `204 No Content`. Fired by the marketplace when a Solution's detail
698        page is rendered in a browser, so publishers can see impressions alongside
699        installs in their Solution analytics.
700        Visibility matches `GET /api/v1/solutions/:solution`: unauthenticated callers
701        (the logged-out marketplace) can only track Solutions published to the public
702        catalog; anything the caller could not retrieve returns 404 and records
703        nothing.
704        Pass `anonymous` (the analytics visitor ID) so logged-out views can be
705        counted as unique viewers. The event's Solution and publisher attribution are
706        resolved server-side from the Solution row never from request input.
707
708        Args:
709            solution: Solution config ID (`cfg_...`) or `lookup_key` of the viewed Solution.
710            input: Request body.
711            input.anonymous: Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the `POST /api/v1/t` events use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session.
712
713        Returns:
714            Empty body. HTTP 204 indicates the Solution view was recorded successfully.
715        """
716        await self._http.request(f"/api/v1/solutions/{solution}/view", method="POST", body=input)
AsyncSolutionResource(http: archastro.platform.runtime.http_client.HttpClient)
358    def __init__(self, http: HttpClient):
359        self._http = http
async def list( self, *, page: int | None = None, page_size: int | None = None, lookup_key: str | None = None, path_prefix: str | None = None, owners: list[str] | None = None) -> archastro.platform.types.common.SolutionListResponse:
361    async def list(
362        self,
363        *,
364        page: int | None = None,
365        page_size: int | None = None,
366        lookup_key: str | None = None,
367        path_prefix: str | None = None,
368        owners: builtins.list[str] | None = None,
369    ) -> SolutionListResponse:
370        """
371        List Solutions
372        Returns a paginated list of Solutions visible to the caller, merging two
373        scopes: app-level Solutions (system-owned rows with no org affiliation,
374        visible to everyone including unauthenticated callers so they can power
375        the public catalog) and org-level Solutions (system-owned rows stamped with
376        the viewer's org ID, included when an authenticated viewer carries an org
377        context). Unauthenticated callers resolve to an app-scoped anonymous viewer
378        and therefore only ever see the app-level scope.
379        Solutions that appear under both scopes are deduplicated by their stable
380        `solution_id` value. The merged entry's `owners` array lists every scope the
381        Solution was found under (`"system"` and/or `"org"`). When the app-level copy
382        has a higher `solution_version` than the org-level copy, the response includes
383        `upgrade_available: true` and `latest_version` so callers can prompt for an
384        upgrade.
385
386        Args:
387            page: Page number to return. Defaults to `1`.
388            page_size: Number of Solutions per page. Defaults to `25`.
389            lookup_key: Filter to the Solution whose `lookup_key` matches exactly.
390            path_prefix: Filter to Solutions whose `virtual_path` starts with this prefix.
391            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.
392
393        Returns:
394            Paginated list of Solution summaries visible to the caller.
395        """
396        query: dict[str, object] = {}
397        if page is not None:
398            query["page"] = page
399        if page_size is not None:
400            query["page_size"] = page_size
401        if lookup_key is not None:
402            query["lookup_key"] = lookup_key
403        if path_prefix is not None:
404            query["path_prefix"] = path_prefix
405        if owners is not None:
406            query["owners"] = owners
407        return await self._http.request(
408            "/api/v1/solutions",
409            query=query,
410            response_type=SolutionListResponse,
411        )

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_key matches exactly.
  • path_prefix: Filter to Solutions whose virtual_path starts 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.

async def create( self, input: SolutionCreateInput) -> archastro.platform.types.common.SolutionImportResponse:
413    async def create(self, input: SolutionCreateInput) -> SolutionImportResponse:
414        """
415        Import a Solution into the library
416        Imports a Solution and its bundled configs (skills, scripts, templates, files)
417        into the library for the target scope. Two mutually exclusive import modes
418        are supported: pass `solution` to re-import an existing system-owned catalog
419        Solution by ID or `lookup_key`, or pass `solution_bundle` to supply a
420        self-contained inline bundle. Exactly one must be present.
421        The operation upserts the bundle in a single transaction. When `dry_run` is
422        `true` the same pipeline runs but the transaction is rolled back no rows are
423        persisted and the response reflects what would have been written. The
424        response shape is the same in both cases: the Solution summary plus
425        `installed_configs` listing each config the import created or would create.
426        Pairs with `POST /api/v1/solutions/:solution/install`: this endpoint puts the
427        Solution into the library; install provisions a runtime resource (Agent,
428        AgentRoutine, AgentTool, etc.) from an already-imported Solution.
429
430        Args:
431            input: Request body.
432            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`.
433            input.org: Organization ID (`org_...`) for the import destination scope.
434            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`.
435            input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`.
436            input.team: Team ID (`team_...`) for the import destination scope.
437            input.user: User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set.
438            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.
439
440        Returns:
441            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.
442        """
443        return await self._http.request(
444            "/api/v1/solutions",
445            method="POST",
446            body=input,
447            response_type=SolutionImportResponse,
448        )

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 to false.
  • input.org: Organization ID (org_...) for the import destination scope.
  • 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.
  • 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 of org, team, or user may be set.
  • 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.
Returns:

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.

async def delete(self, solution: str) -> None:
450    async def delete(self, solution: str) -> None:
451        """
452        Delete a Solution
453        Permanently deletes an imported Solution and all configs bundled with it,
454        including templates, skills, scripts, and files. The deletion runs in a
455        single transaction; provider-stored blobs are swept asynchronously after commit.
456        Org-scope callers (for example, an org admin in the Library settings page) can
457        only delete their org's copy of the Solution. App-scope callers can delete
458        Solutions at either scope. RBAC is enforced inside the core delete flow.
459        Returns `204 No Content` on success.
460
461        Args:
462            solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to delete.
463
464        Returns:
465            Empty body. HTTP 204 indicates the Solution was deleted successfully.
466        """
467        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_...) or lookup_key of the Solution to delete.
Returns:

Empty body. HTTP 204 indicates the Solution was deleted successfully.

async def get(self, solution: str) -> archastro.platform.types.common.SolutionSummary:
469    async def get(self, solution: str) -> SolutionSummary:
470        """
471        Retrieve a Solution
472        Returns a single Solution identified by its config ID (`cfg_...`) or
473        `lookup_key`, in the same summary shape the list endpoint emits. The response
474        always includes a freshly-minted `readme_url` call this endpoint to refresh
475        an expired README token without making any other state change.
476        Visibility matches the list endpoint: app-level Solutions (no org affiliation)
477        are visible to everyone including unauthenticated callers, so the public
478        catalog can render a Solution's detail page logged-out; org-scoped Solutions
479        are only visible to authenticated viewers whose org context matches. Anything
480        else returns 404.
481        When the resolved Solution is org-scoped, the endpoint compares its
482        `solution_version` against the matching app-level copy. If the app-level copy
483        is at a higher version the response includes `upgrade_available: true` and
484        `latest_version`. App-level resolutions always report `upgrade_available: false`.
485
486        Args:
487            solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to retrieve.
488
489        Returns:
490            Solution summary including a freshly-minted `readme_url` token valid for one hour.
491        """
492        return await self._http.request(
493            f"/api/v1/solutions/{solution}",
494            response_type=SolutionSummary,
495        )

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_...) or lookup_key of the Solution to retrieve.
Returns:

Solution summary including a freshly-minted readme_url token valid for one hour.

async def dependents( self, solution: str) -> archastro.platform.types.common.SolutionDependentsResponse:
497    async def dependents(self, solution: str) -> SolutionDependentsResponse:
498        """
499        Preview Solution delete impact
500        Returns a read-only preview of what deleting the specified Solution would
501        affect: the agents that reference the Solution's bundle, and the count of
502        bundled configs that would be orphaned rather than cascade-deleted.
503        Use this endpoint before calling `DELETE /api/v1/solutions/:solution` to
504        surface a warning when live agents depend on the Solution.
505        Visibility scope mirrors the delete endpoint: org-scope viewers see their
506        org's copy; app-scope viewers can inspect either scope.
507
508        Args:
509            solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to inspect.
510
511        Returns:
512            Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete.
513        """
514        return await self._http.request(
515            f"/api/v1/solutions/{solution}/dependents",
516            response_type=SolutionDependentsResponse,
517        )

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_...) or lookup_key of the Solution to inspect.
Returns:

Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete.

async def image( self, solution: str, token: str, *, v: str | None = None, file: str | None = None) -> dict[str, str]:
519    async def image(
520        self, solution: str, token: str, *, v: str | None = None, file: str | None = None
521    ) -> dict[str, str]:
522        """
523        Fetch a Solution cover image or gallery screenshot
524        Returns the raw bytes of a Solution's cover image the bundled asset the
525        Solution body's `image:` field names or, with the optional `file` param,
526        one of its gallery screenshots (`screenshots:` entries). This endpoint backs
527        the `image_url` and `screenshot_urls` fields of catalog payloads (such as
528        `GET /api/v1/solutions`), which anonymous consumers the public
529        marketplace's page cache, OpenGraph scrapers may hold far longer than a
530        signed storage URL lives. Authorization is performed via a short, stable
531        capability `token` rather than an HTTP header, so the URL never expires.
532        The `token` is an HMAC-based capability tied to the Solution config ID. It
533        does not expire, but the endpoint checks at fetch time that the Solution
534        still declares the requested image without `file`, that it still declares
535        a cover; with `file`, that the path is still among the body's declared
536        `image`/`screenshots` so republishing without the asset (or hiding the
537        Solution) turns the URL into a 404. Shared caches may continue serving the
538        old image until the `Cache-Control` max-age of one hour elapses. `v` is an
539        opaque cache key minted alongside the token; it changes when the Solution
540        changes and is ignored by verification.
541        All failure modes invalid config ID, invalid token, hidden Solution, no
542        declared cover, a `file` outside the declared set, or an asset that doesn't
543        resolve to a bundled image return a uniform `404` to avoid acting as an
544        existence oracle.
545
546        Args:
547            solution: Solution config ID (`cfg_...`) whose cover image to fetch.
548            token: HMAC capability token authorizing access to this Solution's cover. Obtained from the `image_url` minted when the Solution was serialized.
549            v: Opaque cache key minted alongside the token; changes when the Solution changes. Ignored by token verification.
550            file: Bundled asset path of the image to serve (for example `images/setup.png`). Must be one of the paths the Solution body currently declares in `image`/`screenshots` anything else is a `404`. When absent the declared cover (`image:`) is served.
551
552        Returns:
553            Raw cover image bytes, served with the asset's image content type.
554        """
555        query: dict[str, object] = {}
556        query["token"] = token
557        if v is not None:
558            query["v"] = v
559        if file is not None:
560            query["file"] = file
561        return await self._http.request_raw(f"/api/v1/solutions/{solution}/image", query=query)

Fetch a Solution cover image or gallery screenshot Returns the raw bytes of a Solution's cover image the bundled asset the Solution body's image: field names or, with the optional file param, one of its gallery screenshots (screenshots: entries). This endpoint backs the image_url and screenshot_urls fields of catalog payloads (such as GET /api/v1/solutions), which anonymous consumers the public marketplace's page cache, OpenGraph scrapers may hold far longer than a signed storage URL lives. Authorization is performed via a short, stable capability token rather than an HTTP header, so the URL never expires. The token is an HMAC-based capability tied to the Solution config ID. It does not expire, but the endpoint checks at fetch time that the Solution still declares the requested image without file, that it still declares a cover; with file, that the path is still among the body's declared image/screenshots so republishing without the asset (or hiding the Solution) turns the URL into a 404. Shared caches may continue serving the old image until the Cache-Control max-age of one hour elapses. v is an opaque cache key minted alongside the token; it changes when the Solution changes and is ignored by verification. All failure modes invalid config ID, invalid token, hidden Solution, no declared cover, a file outside the declared set, or an asset that doesn't resolve to a bundled image return a uniform 404 to avoid acting as an existence oracle.

Arguments:
  • solution: Solution config ID (cfg_...) whose cover image to fetch.
  • token: HMAC capability token authorizing access to this Solution's cover. Obtained from the image_url minted when the Solution was serialized.
  • v: Opaque cache key minted alongside the token; changes when the Solution changes. Ignored by token verification.
  • file: Bundled asset path of the image to serve (for example images/setup.png). Must be one of the paths the Solution body currently declares in image/screenshots anything else is a 404. When absent the declared cover (image:) is served.
Returns:

Raw cover image bytes, served with the asset's image content type.

async def install( self, solution: str, input: SolutionInstallInput) -> archastro.platform.types.common.SolutionInstallResponse:
563    async def install(self, solution: str, input: SolutionInstallInput) -> SolutionInstallResponse:
564        """
565        Install a Solution
566        Provisions a runtime resource from an already-imported Solution. The type of
567        resource created depends on the template the Solution wraps: an
568        `AgentTemplate` produces an Agent, an `AutomationTemplate` produces an
569        Automation, and attachment templates (`AgentRoutineTemplate`,
570        `AgentToolTemplate`, `AgentSkillTemplate`, `AgentComputerTemplate`) attach a
571        sub-resource to an existing Agent specified by `target`.
572        For Solutions that bundle more than one template, pass `template` (the ID or
573        `lookup_key` of the desired template) to select which one to provision.
574        Single-template Solutions do not require `template`.
575        Pairs with `POST /api/v1/solutions` (import): import puts the Solution into
576        the library; install provisions a runtime resource from it.
577
578        Args:
579            solution: Config ID (`cfg_...`) or `lookup_key` of the already-imported Solution to install from.
580            input: Request body.
581            input.acl: Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team.
582            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.
583            input.details: Template-specific install options selected by the `type` discriminator. AutomationTemplate installs accept `{type: "automation", prefills: ...}`.
584            input.install_inputs: Values applied to parameterized AgentTemplate prose during a root Agent install. Use `{values: {customer_label: "Acme"}}`.
585            input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's `agent_key`).
586            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.
587            input.name: Display name override for a provisioned Agent or Automation. Ignored for attachment Solutions.
588            input.org: Organization ID (`org_...`) for the install destination scope.
589            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.
590            input.team: Team ID (`team_...`) for the install destination scope.
591            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.
592            input.user: User ID (`usr_...`) for the install destination scope.
593
594        Returns:
595            The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID.
596        """
597        return await self._http.request(
598            f"/api/v1/solutions/{solution}/install",
599            method="POST",
600            body=input,
601            response_type=SolutionInstallResponse,
602        )

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_...) or lookup_key of the already-imported Solution to install from.
  • input: Request body.
  • input.acl: Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team.
  • 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.
  • input.details: Template-specific install options selected by the type discriminator. AutomationTemplate installs accept {type: "automation", prefills: ...}.
  • input.install_inputs: Values applied to parameterized AgentTemplate prose during a root Agent install. Use {values: {customer_label: "Acme"}}.
  • 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 a provisioned Agent or Automation. Ignored for attachment Solutions.
  • input.org: Organization ID (org_...) for the install destination scope.
  • 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.
  • input.team: Team ID (team_...) for the install destination scope.
  • 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.
  • 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.

async def readme( self, solution: str, token: str, *, file: str | None = None) -> dict[str, str]:
604    async def readme(self, solution: str, token: str, *, file: str | None = None) -> dict[str, str]:
605        """
606        Retrieve a Solution README or asset
607        Serves the README markdown or a bundled asset for an imported Solution. Both
608        modes use the same path and require a short-lived signed `token` in the query
609        string rather than an `Authorization` header, so browsers can load asset URLs
610        directly from `<img src>` attributes without custom request logic.
611        When `file` is omitted the response is the Solution's `readme` field rendered
612        as `text/markdown`. All local asset references in the markdown are rewritten to
613        point back at this endpoint with `?file=PATH&token=TOKEN` so browsers can load
614        images inline without additional authentication.
615        When `file` is set the response is the raw bytes of the matching asset (a File
616        child whose `relative_path` equals `PATH`, or an inline `assets` entry by
617        name) with the asset's stored `Content-Type`.
618        Tokens are scoped to a single Solution, carry the viewer's app, org, and
619        sandbox context from the time they were minted, and expire after one hour.
620        Obtain a fresh token by calling `GET /api/v1/solutions/:solution`, which
621        always returns a newly minted `readme_url`.
622
623        Args:
624            solution: Solution config ID (`cfg_...`) identifying the Solution whose README or asset to retrieve.
625            token: Signed URL token minted by the list or show endpoint. Expires after one hour.
626            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.
627
628        Returns:
629            README markdown (`text/markdown`) when `file` is omitted, or the raw asset bytes with the asset's `Content-Type` when `file` is set.
630        """
631        query: dict[str, object] = {}
632        query["token"] = token
633        if file is not None:
634            query["file"] = file
635        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 real Content-Type; when absent the response is the README markdown.
Returns:

README markdown (text/markdown) when file is omitted, or the raw asset bytes with the asset's Content-Type when file is set.

async def reconcile_product_knowledge( self, solution: str) -> archastro.platform.types.common.ProductKnowledgeReconcileResponse:
637    async def reconcile_product_knowledge(self, solution: str) -> ProductKnowledgeReconcileResponse:
638        """
639        Reconcile Customer Management product knowledge
640        Recomputes every stamped customer's knowledge-search source allowlist
641        from the template-resolved customer refs plus the install's current marked
642        product-knowledge sources. Safe to retry; partial failures are returned by
643        customer key rather than hidden behind an aggregate success.
644
645        Args:
646            solution: Installed Customer Management Solution config (`cfg_...`) to reconcile.
647
648        Returns:
649            Per-customer convergence result for this installed solution.
650        """
651        return await self._http.request(
652            f"/api/v1/solutions/{solution}/reconcile_product_knowledge",
653            method="POST",
654            response_type=ProductKnowledgeReconcileResponse,
655        )

Reconcile Customer Management product knowledge Recomputes every stamped customer's knowledge-search source allowlist from the template-resolved customer refs plus the install's current marked product-knowledge sources. Safe to retry; partial failures are returned by customer key rather than hidden behind an aggregate success.

Arguments:
  • solution: Installed Customer Management Solution config (cfg_...) to reconcile.
Returns:

Per-customer convergence result for this installed solution.

async def upgrade( self, solution: str, input: SolutionUpgradeInput) -> archastro.platform.types.common.SolutionUpgradeResponse:
657    async def upgrade(self, solution: str, input: SolutionUpgradeInput) -> SolutionUpgradeResponse:
658        """
659        Upgrade an installed Solution
660        Applies an incoming bundle to an already-installed Solution in a single atomic
661        transaction, bringing its configs in line with the new bundle. Config IDs are
662        preserved across the upgrade. Configs that existed in the old bundle but are
663        absent from the new one are orphaned (top-level) or hard-deleted (child rows).
664        Two mutually exclusive source modes: pass `target_solution` to pull the
665        incoming bundle from an existing Solution by ID or `lookup_key`, or pass
666        `solution_bundle` to supply a complete inline bundle directly. Exactly one
667        must be present.
668        When `dry_run` is `true` the full diff is computed and returned but no
669        changes are written. Pass the dry-run response's `review_fingerprint` as
670        `expected_review_fingerprint` when applying to guard against the bundle
671        changing between review and apply.
672
673        Args:
674            solution: Config ID (`cfg_...`) or `lookup_key` of the currently installed Solution to upgrade.
675            input: Request body.
676            input.allow_downgrade: When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`.
677            input.dry_run: When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`.
678            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.
679            input.org: Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this.
680            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`.
681            input.target_solution: Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`.
682
683        Returns:
684            Updated Solution plus the full upgrade diff.
685        """
686        return await self._http.request(
687            f"/api/v1/solutions/{solution}/upgrade",
688            method="POST",
689            body=input,
690            response_type=SolutionUpgradeResponse,
691        )

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_...) or lookup_key of the currently installed Solution to upgrade.
  • input: Request body.
  • input.allow_downgrade: When true, permits an incoming solution_version lower than the currently installed version. Defaults to false.
  • input.dry_run: When true, computes and returns the full upgrade diff without persisting any changes. Defaults to false.
  • 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.
  • input.org: Organization ID (org_...) used to resolve org-scoped lookup_key values. 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_...) or lookup_key of the Solution to use as the incoming upgrade source. Mutually exclusive with solution_bundle.
Returns:

Updated Solution plus the full upgrade diff.

async def view( self, solution: str, input: SolutionViewInput) -> None:
693    async def view(self, solution: str, input: SolutionViewInput) -> None:
694        """
695        Track a Solution detail-page view
696        Records a `solution_viewed` analytics event for the identified Solution and
697        returns `204 No Content`. Fired by the marketplace when a Solution's detail
698        page is rendered in a browser, so publishers can see impressions alongside
699        installs in their Solution analytics.
700        Visibility matches `GET /api/v1/solutions/:solution`: unauthenticated callers
701        (the logged-out marketplace) can only track Solutions published to the public
702        catalog; anything the caller could not retrieve returns 404 and records
703        nothing.
704        Pass `anonymous` (the analytics visitor ID) so logged-out views can be
705        counted as unique viewers. The event's Solution and publisher attribution are
706        resolved server-side from the Solution row never from request input.
707
708        Args:
709            solution: Solution config ID (`cfg_...`) or `lookup_key` of the viewed Solution.
710            input: Request body.
711            input.anonymous: Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the `POST /api/v1/t` events use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session.
712
713        Returns:
714            Empty body. HTTP 204 indicates the Solution view was recorded successfully.
715        """
716        await self._http.request(f"/api/v1/solutions/{solution}/view", method="POST", body=input)

Track a Solution detail-page view Records a solution_viewed analytics event for the identified Solution and returns 204 No Content. Fired by the marketplace when a Solution's detail page is rendered in a browser, so publishers can see impressions alongside installs in their Solution analytics. Visibility matches GET /api/v1/solutions/:solution: unauthenticated callers (the logged-out marketplace) can only track Solutions published to the public catalog; anything the caller could not retrieve returns 404 and records nothing. Pass anonymous (the analytics visitor ID) so logged-out views can be counted as unique viewers. The event's Solution and publisher attribution are resolved server-side from the Solution row never from request input.

Arguments:
  • solution: Solution config ID (cfg_...) or lookup_key of the viewed Solution.
  • input: Request body.
  • input.anonymous: Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the POST /api/v1/t events use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session.
Returns:

Empty body. HTTP 204 indicates the Solution view was recorded successfully.

class SolutionResource:
 719class SolutionResource:
 720    def __init__(self, http: SyncHttpClient):
 721        self._http = http
 722
 723    def list(
 724        self,
 725        *,
 726        page: int | None = None,
 727        page_size: int | None = None,
 728        lookup_key: str | None = None,
 729        path_prefix: str | None = None,
 730        owners: builtins.list[str] | None = None,
 731    ) -> SolutionListResponse:
 732        """
 733        List Solutions
 734        Returns a paginated list of Solutions visible to the caller, merging two
 735        scopes: app-level Solutions (system-owned rows with no org affiliation,
 736        visible to everyone including unauthenticated callers so they can power
 737        the public catalog) and org-level Solutions (system-owned rows stamped with
 738        the viewer's org ID, included when an authenticated viewer carries an org
 739        context). Unauthenticated callers resolve to an app-scoped anonymous viewer
 740        and therefore only ever see the app-level scope.
 741        Solutions that appear under both scopes are deduplicated by their stable
 742        `solution_id` value. The merged entry's `owners` array lists every scope the
 743        Solution was found under (`"system"` and/or `"org"`). When the app-level copy
 744        has a higher `solution_version` than the org-level copy, the response includes
 745        `upgrade_available: true` and `latest_version` so callers can prompt for an
 746        upgrade.
 747
 748        Args:
 749            page: Page number to return. Defaults to `1`.
 750            page_size: Number of Solutions per page. Defaults to `25`.
 751            lookup_key: Filter to the Solution whose `lookup_key` matches exactly.
 752            path_prefix: Filter to Solutions whose `virtual_path` starts with this prefix.
 753            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.
 754
 755        Returns:
 756            Paginated list of Solution summaries visible to the caller.
 757        """
 758        query: dict[str, object] = {}
 759        if page is not None:
 760            query["page"] = page
 761        if page_size is not None:
 762            query["page_size"] = page_size
 763        if lookup_key is not None:
 764            query["lookup_key"] = lookup_key
 765        if path_prefix is not None:
 766            query["path_prefix"] = path_prefix
 767        if owners is not None:
 768            query["owners"] = owners
 769        return self._http.request(
 770            "/api/v1/solutions",
 771            query=query,
 772            response_type=SolutionListResponse,
 773        )
 774
 775    def create(self, input: SolutionCreateInput) -> SolutionImportResponse:
 776        """
 777        Import a Solution into the library
 778        Imports a Solution and its bundled configs (skills, scripts, templates, files)
 779        into the library for the target scope. Two mutually exclusive import modes
 780        are supported: pass `solution` to re-import an existing system-owned catalog
 781        Solution by ID or `lookup_key`, or pass `solution_bundle` to supply a
 782        self-contained inline bundle. Exactly one must be present.
 783        The operation upserts the bundle in a single transaction. When `dry_run` is
 784        `true` the same pipeline runs but the transaction is rolled back no rows are
 785        persisted and the response reflects what would have been written. The
 786        response shape is the same in both cases: the Solution summary plus
 787        `installed_configs` listing each config the import created or would create.
 788        Pairs with `POST /api/v1/solutions/:solution/install`: this endpoint puts the
 789        Solution into the library; install provisions a runtime resource (Agent,
 790        AgentRoutine, AgentTool, etc.) from an already-imported Solution.
 791
 792        Args:
 793            input: Request body.
 794            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`.
 795            input.org: Organization ID (`org_...`) for the import destination scope.
 796            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`.
 797            input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`.
 798            input.team: Team ID (`team_...`) for the import destination scope.
 799            input.user: User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set.
 800            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.
 801
 802        Returns:
 803            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.
 804        """
 805        return self._http.request(
 806            "/api/v1/solutions",
 807            method="POST",
 808            body=input,
 809            response_type=SolutionImportResponse,
 810        )
 811
 812    def delete(self, solution: str) -> None:
 813        """
 814        Delete a Solution
 815        Permanently deletes an imported Solution and all configs bundled with it,
 816        including templates, skills, scripts, and files. The deletion runs in a
 817        single transaction; provider-stored blobs are swept asynchronously after commit.
 818        Org-scope callers (for example, an org admin in the Library settings page) can
 819        only delete their org's copy of the Solution. App-scope callers can delete
 820        Solutions at either scope. RBAC is enforced inside the core delete flow.
 821        Returns `204 No Content` on success.
 822
 823        Args:
 824            solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to delete.
 825
 826        Returns:
 827            Empty body. HTTP 204 indicates the Solution was deleted successfully.
 828        """
 829        self._http.request(f"/api/v1/solutions/{solution}", method="DELETE")
 830
 831    def get(self, solution: str) -> SolutionSummary:
 832        """
 833        Retrieve a Solution
 834        Returns a single Solution identified by its config ID (`cfg_...`) or
 835        `lookup_key`, in the same summary shape the list endpoint emits. The response
 836        always includes a freshly-minted `readme_url` call this endpoint to refresh
 837        an expired README token without making any other state change.
 838        Visibility matches the list endpoint: app-level Solutions (no org affiliation)
 839        are visible to everyone including unauthenticated callers, so the public
 840        catalog can render a Solution's detail page logged-out; org-scoped Solutions
 841        are only visible to authenticated viewers whose org context matches. Anything
 842        else returns 404.
 843        When the resolved Solution is org-scoped, the endpoint compares its
 844        `solution_version` against the matching app-level copy. If the app-level copy
 845        is at a higher version the response includes `upgrade_available: true` and
 846        `latest_version`. App-level resolutions always report `upgrade_available: false`.
 847
 848        Args:
 849            solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to retrieve.
 850
 851        Returns:
 852            Solution summary including a freshly-minted `readme_url` token valid for one hour.
 853        """
 854        return self._http.request(f"/api/v1/solutions/{solution}", response_type=SolutionSummary)
 855
 856    def dependents(self, solution: str) -> SolutionDependentsResponse:
 857        """
 858        Preview Solution delete impact
 859        Returns a read-only preview of what deleting the specified Solution would
 860        affect: the agents that reference the Solution's bundle, and the count of
 861        bundled configs that would be orphaned rather than cascade-deleted.
 862        Use this endpoint before calling `DELETE /api/v1/solutions/:solution` to
 863        surface a warning when live agents depend on the Solution.
 864        Visibility scope mirrors the delete endpoint: org-scope viewers see their
 865        org's copy; app-scope viewers can inspect either scope.
 866
 867        Args:
 868            solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to inspect.
 869
 870        Returns:
 871            Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete.
 872        """
 873        return self._http.request(
 874            f"/api/v1/solutions/{solution}/dependents",
 875            response_type=SolutionDependentsResponse,
 876        )
 877
 878    def image(
 879        self, solution: str, token: str, *, v: str | None = None, file: str | None = None
 880    ) -> dict[str, str]:
 881        """
 882        Fetch a Solution cover image or gallery screenshot
 883        Returns the raw bytes of a Solution's cover image the bundled asset the
 884        Solution body's `image:` field names or, with the optional `file` param,
 885        one of its gallery screenshots (`screenshots:` entries). This endpoint backs
 886        the `image_url` and `screenshot_urls` fields of catalog payloads (such as
 887        `GET /api/v1/solutions`), which anonymous consumers the public
 888        marketplace's page cache, OpenGraph scrapers may hold far longer than a
 889        signed storage URL lives. Authorization is performed via a short, stable
 890        capability `token` rather than an HTTP header, so the URL never expires.
 891        The `token` is an HMAC-based capability tied to the Solution config ID. It
 892        does not expire, but the endpoint checks at fetch time that the Solution
 893        still declares the requested image without `file`, that it still declares
 894        a cover; with `file`, that the path is still among the body's declared
 895        `image`/`screenshots` so republishing without the asset (or hiding the
 896        Solution) turns the URL into a 404. Shared caches may continue serving the
 897        old image until the `Cache-Control` max-age of one hour elapses. `v` is an
 898        opaque cache key minted alongside the token; it changes when the Solution
 899        changes and is ignored by verification.
 900        All failure modes invalid config ID, invalid token, hidden Solution, no
 901        declared cover, a `file` outside the declared set, or an asset that doesn't
 902        resolve to a bundled image return a uniform `404` to avoid acting as an
 903        existence oracle.
 904
 905        Args:
 906            solution: Solution config ID (`cfg_...`) whose cover image to fetch.
 907            token: HMAC capability token authorizing access to this Solution's cover. Obtained from the `image_url` minted when the Solution was serialized.
 908            v: Opaque cache key minted alongside the token; changes when the Solution changes. Ignored by token verification.
 909            file: Bundled asset path of the image to serve (for example `images/setup.png`). Must be one of the paths the Solution body currently declares in `image`/`screenshots` anything else is a `404`. When absent the declared cover (`image:`) is served.
 910
 911        Returns:
 912            Raw cover image bytes, served with the asset's image content type.
 913        """
 914        query: dict[str, object] = {}
 915        query["token"] = token
 916        if v is not None:
 917            query["v"] = v
 918        if file is not None:
 919            query["file"] = file
 920        return self._http.request_raw(f"/api/v1/solutions/{solution}/image", query=query)
 921
 922    def install(self, solution: str, input: SolutionInstallInput) -> SolutionInstallResponse:
 923        """
 924        Install a Solution
 925        Provisions a runtime resource from an already-imported Solution. The type of
 926        resource created depends on the template the Solution wraps: an
 927        `AgentTemplate` produces an Agent, an `AutomationTemplate` produces an
 928        Automation, and attachment templates (`AgentRoutineTemplate`,
 929        `AgentToolTemplate`, `AgentSkillTemplate`, `AgentComputerTemplate`) attach a
 930        sub-resource to an existing Agent specified by `target`.
 931        For Solutions that bundle more than one template, pass `template` (the ID or
 932        `lookup_key` of the desired template) to select which one to provision.
 933        Single-template Solutions do not require `template`.
 934        Pairs with `POST /api/v1/solutions` (import): import puts the Solution into
 935        the library; install provisions a runtime resource from it.
 936
 937        Args:
 938            solution: Config ID (`cfg_...`) or `lookup_key` of the already-imported Solution to install from.
 939            input: Request body.
 940            input.acl: Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team.
 941            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.
 942            input.details: Template-specific install options selected by the `type` discriminator. AutomationTemplate installs accept `{type: "automation", prefills: ...}`.
 943            input.install_inputs: Values applied to parameterized AgentTemplate prose during a root Agent install. Use `{values: {customer_label: "Acme"}}`.
 944            input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's `agent_key`).
 945            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.
 946            input.name: Display name override for a provisioned Agent or Automation. Ignored for attachment Solutions.
 947            input.org: Organization ID (`org_...`) for the install destination scope.
 948            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.
 949            input.team: Team ID (`team_...`) for the install destination scope.
 950            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.
 951            input.user: User ID (`usr_...`) for the install destination scope.
 952
 953        Returns:
 954            The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID.
 955        """
 956        return self._http.request(
 957            f"/api/v1/solutions/{solution}/install",
 958            method="POST",
 959            body=input,
 960            response_type=SolutionInstallResponse,
 961        )
 962
 963    def readme(self, solution: str, token: str, *, file: str | None = None) -> dict[str, str]:
 964        """
 965        Retrieve a Solution README or asset
 966        Serves the README markdown or a bundled asset for an imported Solution. Both
 967        modes use the same path and require a short-lived signed `token` in the query
 968        string rather than an `Authorization` header, so browsers can load asset URLs
 969        directly from `<img src>` attributes without custom request logic.
 970        When `file` is omitted the response is the Solution's `readme` field rendered
 971        as `text/markdown`. All local asset references in the markdown are rewritten to
 972        point back at this endpoint with `?file=PATH&token=TOKEN` so browsers can load
 973        images inline without additional authentication.
 974        When `file` is set the response is the raw bytes of the matching asset (a File
 975        child whose `relative_path` equals `PATH`, or an inline `assets` entry by
 976        name) with the asset's stored `Content-Type`.
 977        Tokens are scoped to a single Solution, carry the viewer's app, org, and
 978        sandbox context from the time they were minted, and expire after one hour.
 979        Obtain a fresh token by calling `GET /api/v1/solutions/:solution`, which
 980        always returns a newly minted `readme_url`.
 981
 982        Args:
 983            solution: Solution config ID (`cfg_...`) identifying the Solution whose README or asset to retrieve.
 984            token: Signed URL token minted by the list or show endpoint. Expires after one hour.
 985            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.
 986
 987        Returns:
 988            README markdown (`text/markdown`) when `file` is omitted, or the raw asset bytes with the asset's `Content-Type` when `file` is set.
 989        """
 990        query: dict[str, object] = {}
 991        query["token"] = token
 992        if file is not None:
 993            query["file"] = file
 994        return self._http.request_raw(f"/api/v1/solutions/{solution}/readme", query=query)
 995
 996    def reconcile_product_knowledge(self, solution: str) -> ProductKnowledgeReconcileResponse:
 997        """
 998        Reconcile Customer Management product knowledge
 999        Recomputes every stamped customer's knowledge-search source allowlist
1000        from the template-resolved customer refs plus the install's current marked
1001        product-knowledge sources. Safe to retry; partial failures are returned by
1002        customer key rather than hidden behind an aggregate success.
1003
1004        Args:
1005            solution: Installed Customer Management Solution config (`cfg_...`) to reconcile.
1006
1007        Returns:
1008            Per-customer convergence result for this installed solution.
1009        """
1010        return self._http.request(
1011            f"/api/v1/solutions/{solution}/reconcile_product_knowledge",
1012            method="POST",
1013            response_type=ProductKnowledgeReconcileResponse,
1014        )
1015
1016    def upgrade(self, solution: str, input: SolutionUpgradeInput) -> SolutionUpgradeResponse:
1017        """
1018        Upgrade an installed Solution
1019        Applies an incoming bundle to an already-installed Solution in a single atomic
1020        transaction, bringing its configs in line with the new bundle. Config IDs are
1021        preserved across the upgrade. Configs that existed in the old bundle but are
1022        absent from the new one are orphaned (top-level) or hard-deleted (child rows).
1023        Two mutually exclusive source modes: pass `target_solution` to pull the
1024        incoming bundle from an existing Solution by ID or `lookup_key`, or pass
1025        `solution_bundle` to supply a complete inline bundle directly. Exactly one
1026        must be present.
1027        When `dry_run` is `true` the full diff is computed and returned but no
1028        changes are written. Pass the dry-run response's `review_fingerprint` as
1029        `expected_review_fingerprint` when applying to guard against the bundle
1030        changing between review and apply.
1031
1032        Args:
1033            solution: Config ID (`cfg_...`) or `lookup_key` of the currently installed Solution to upgrade.
1034            input: Request body.
1035            input.allow_downgrade: When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`.
1036            input.dry_run: When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`.
1037            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.
1038            input.org: Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this.
1039            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`.
1040            input.target_solution: Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`.
1041
1042        Returns:
1043            Updated Solution plus the full upgrade diff.
1044        """
1045        return self._http.request(
1046            f"/api/v1/solutions/{solution}/upgrade",
1047            method="POST",
1048            body=input,
1049            response_type=SolutionUpgradeResponse,
1050        )
1051
1052    def view(self, solution: str, input: SolutionViewInput) -> None:
1053        """
1054        Track a Solution detail-page view
1055        Records a `solution_viewed` analytics event for the identified Solution and
1056        returns `204 No Content`. Fired by the marketplace when a Solution's detail
1057        page is rendered in a browser, so publishers can see impressions alongside
1058        installs in their Solution analytics.
1059        Visibility matches `GET /api/v1/solutions/:solution`: unauthenticated callers
1060        (the logged-out marketplace) can only track Solutions published to the public
1061        catalog; anything the caller could not retrieve returns 404 and records
1062        nothing.
1063        Pass `anonymous` (the analytics visitor ID) so logged-out views can be
1064        counted as unique viewers. The event's Solution and publisher attribution are
1065        resolved server-side from the Solution row never from request input.
1066
1067        Args:
1068            solution: Solution config ID (`cfg_...`) or `lookup_key` of the viewed Solution.
1069            input: Request body.
1070            input.anonymous: Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the `POST /api/v1/t` events use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session.
1071
1072        Returns:
1073            Empty body. HTTP 204 indicates the Solution view was recorded successfully.
1074        """
1075        self._http.request(f"/api/v1/solutions/{solution}/view", method="POST", body=input)
SolutionResource(http: archastro.platform.runtime.http_client.SyncHttpClient)
720    def __init__(self, http: SyncHttpClient):
721        self._http = http
def list( self, *, page: int | None = None, page_size: int | None = None, lookup_key: str | None = None, path_prefix: str | None = None, owners: list[str] | None = None) -> archastro.platform.types.common.SolutionListResponse:
723    def list(
724        self,
725        *,
726        page: int | None = None,
727        page_size: int | None = None,
728        lookup_key: str | None = None,
729        path_prefix: str | None = None,
730        owners: builtins.list[str] | None = None,
731    ) -> SolutionListResponse:
732        """
733        List Solutions
734        Returns a paginated list of Solutions visible to the caller, merging two
735        scopes: app-level Solutions (system-owned rows with no org affiliation,
736        visible to everyone including unauthenticated callers so they can power
737        the public catalog) and org-level Solutions (system-owned rows stamped with
738        the viewer's org ID, included when an authenticated viewer carries an org
739        context). Unauthenticated callers resolve to an app-scoped anonymous viewer
740        and therefore only ever see the app-level scope.
741        Solutions that appear under both scopes are deduplicated by their stable
742        `solution_id` value. The merged entry's `owners` array lists every scope the
743        Solution was found under (`"system"` and/or `"org"`). When the app-level copy
744        has a higher `solution_version` than the org-level copy, the response includes
745        `upgrade_available: true` and `latest_version` so callers can prompt for an
746        upgrade.
747
748        Args:
749            page: Page number to return. Defaults to `1`.
750            page_size: Number of Solutions per page. Defaults to `25`.
751            lookup_key: Filter to the Solution whose `lookup_key` matches exactly.
752            path_prefix: Filter to Solutions whose `virtual_path` starts with this prefix.
753            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.
754
755        Returns:
756            Paginated list of Solution summaries visible to the caller.
757        """
758        query: dict[str, object] = {}
759        if page is not None:
760            query["page"] = page
761        if page_size is not None:
762            query["page_size"] = page_size
763        if lookup_key is not None:
764            query["lookup_key"] = lookup_key
765        if path_prefix is not None:
766            query["path_prefix"] = path_prefix
767        if owners is not None:
768            query["owners"] = owners
769        return self._http.request(
770            "/api/v1/solutions",
771            query=query,
772            response_type=SolutionListResponse,
773        )

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_key matches exactly.
  • path_prefix: Filter to Solutions whose virtual_path starts 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.

775    def create(self, input: SolutionCreateInput) -> SolutionImportResponse:
776        """
777        Import a Solution into the library
778        Imports a Solution and its bundled configs (skills, scripts, templates, files)
779        into the library for the target scope. Two mutually exclusive import modes
780        are supported: pass `solution` to re-import an existing system-owned catalog
781        Solution by ID or `lookup_key`, or pass `solution_bundle` to supply a
782        self-contained inline bundle. Exactly one must be present.
783        The operation upserts the bundle in a single transaction. When `dry_run` is
784        `true` the same pipeline runs but the transaction is rolled back no rows are
785        persisted and the response reflects what would have been written. The
786        response shape is the same in both cases: the Solution summary plus
787        `installed_configs` listing each config the import created or would create.
788        Pairs with `POST /api/v1/solutions/:solution/install`: this endpoint puts the
789        Solution into the library; install provisions a runtime resource (Agent,
790        AgentRoutine, AgentTool, etc.) from an already-imported Solution.
791
792        Args:
793            input: Request body.
794            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`.
795            input.org: Organization ID (`org_...`) for the import destination scope.
796            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`.
797            input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`.
798            input.team: Team ID (`team_...`) for the import destination scope.
799            input.user: User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set.
800            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.
801
802        Returns:
803            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.
804        """
805        return self._http.request(
806            "/api/v1/solutions",
807            method="POST",
808            body=input,
809            response_type=SolutionImportResponse,
810        )

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 to false.
  • input.org: Organization ID (org_...) for the import destination scope.
  • 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.
  • 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 of org, team, or user may be set.
  • 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.
Returns:

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.

def delete(self, solution: str) -> None:
812    def delete(self, solution: str) -> None:
813        """
814        Delete a Solution
815        Permanently deletes an imported Solution and all configs bundled with it,
816        including templates, skills, scripts, and files. The deletion runs in a
817        single transaction; provider-stored blobs are swept asynchronously after commit.
818        Org-scope callers (for example, an org admin in the Library settings page) can
819        only delete their org's copy of the Solution. App-scope callers can delete
820        Solutions at either scope. RBAC is enforced inside the core delete flow.
821        Returns `204 No Content` on success.
822
823        Args:
824            solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to delete.
825
826        Returns:
827            Empty body. HTTP 204 indicates the Solution was deleted successfully.
828        """
829        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_...) or lookup_key of the Solution to delete.
Returns:

Empty body. HTTP 204 indicates the Solution was deleted successfully.

def get(self, solution: str) -> archastro.platform.types.common.SolutionSummary:
831    def get(self, solution: str) -> SolutionSummary:
832        """
833        Retrieve a Solution
834        Returns a single Solution identified by its config ID (`cfg_...`) or
835        `lookup_key`, in the same summary shape the list endpoint emits. The response
836        always includes a freshly-minted `readme_url` call this endpoint to refresh
837        an expired README token without making any other state change.
838        Visibility matches the list endpoint: app-level Solutions (no org affiliation)
839        are visible to everyone including unauthenticated callers, so the public
840        catalog can render a Solution's detail page logged-out; org-scoped Solutions
841        are only visible to authenticated viewers whose org context matches. Anything
842        else returns 404.
843        When the resolved Solution is org-scoped, the endpoint compares its
844        `solution_version` against the matching app-level copy. If the app-level copy
845        is at a higher version the response includes `upgrade_available: true` and
846        `latest_version`. App-level resolutions always report `upgrade_available: false`.
847
848        Args:
849            solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to retrieve.
850
851        Returns:
852            Solution summary including a freshly-minted `readme_url` token valid for one hour.
853        """
854        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_...) or lookup_key of the Solution to retrieve.
Returns:

Solution summary including a freshly-minted readme_url token valid for one hour.

def dependents( self, solution: str) -> archastro.platform.types.common.SolutionDependentsResponse:
856    def dependents(self, solution: str) -> SolutionDependentsResponse:
857        """
858        Preview Solution delete impact
859        Returns a read-only preview of what deleting the specified Solution would
860        affect: the agents that reference the Solution's bundle, and the count of
861        bundled configs that would be orphaned rather than cascade-deleted.
862        Use this endpoint before calling `DELETE /api/v1/solutions/:solution` to
863        surface a warning when live agents depend on the Solution.
864        Visibility scope mirrors the delete endpoint: org-scope viewers see their
865        org's copy; app-scope viewers can inspect either scope.
866
867        Args:
868            solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to inspect.
869
870        Returns:
871            Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete.
872        """
873        return self._http.request(
874            f"/api/v1/solutions/{solution}/dependents",
875            response_type=SolutionDependentsResponse,
876        )

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_...) or lookup_key of the Solution to inspect.
Returns:

Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete.

def image( self, solution: str, token: str, *, v: str | None = None, file: str | None = None) -> dict[str, str]:
878    def image(
879        self, solution: str, token: str, *, v: str | None = None, file: str | None = None
880    ) -> dict[str, str]:
881        """
882        Fetch a Solution cover image or gallery screenshot
883        Returns the raw bytes of a Solution's cover image the bundled asset the
884        Solution body's `image:` field names or, with the optional `file` param,
885        one of its gallery screenshots (`screenshots:` entries). This endpoint backs
886        the `image_url` and `screenshot_urls` fields of catalog payloads (such as
887        `GET /api/v1/solutions`), which anonymous consumers the public
888        marketplace's page cache, OpenGraph scrapers may hold far longer than a
889        signed storage URL lives. Authorization is performed via a short, stable
890        capability `token` rather than an HTTP header, so the URL never expires.
891        The `token` is an HMAC-based capability tied to the Solution config ID. It
892        does not expire, but the endpoint checks at fetch time that the Solution
893        still declares the requested image without `file`, that it still declares
894        a cover; with `file`, that the path is still among the body's declared
895        `image`/`screenshots` so republishing without the asset (or hiding the
896        Solution) turns the URL into a 404. Shared caches may continue serving the
897        old image until the `Cache-Control` max-age of one hour elapses. `v` is an
898        opaque cache key minted alongside the token; it changes when the Solution
899        changes and is ignored by verification.
900        All failure modes invalid config ID, invalid token, hidden Solution, no
901        declared cover, a `file` outside the declared set, or an asset that doesn't
902        resolve to a bundled image return a uniform `404` to avoid acting as an
903        existence oracle.
904
905        Args:
906            solution: Solution config ID (`cfg_...`) whose cover image to fetch.
907            token: HMAC capability token authorizing access to this Solution's cover. Obtained from the `image_url` minted when the Solution was serialized.
908            v: Opaque cache key minted alongside the token; changes when the Solution changes. Ignored by token verification.
909            file: Bundled asset path of the image to serve (for example `images/setup.png`). Must be one of the paths the Solution body currently declares in `image`/`screenshots` anything else is a `404`. When absent the declared cover (`image:`) is served.
910
911        Returns:
912            Raw cover image bytes, served with the asset's image content type.
913        """
914        query: dict[str, object] = {}
915        query["token"] = token
916        if v is not None:
917            query["v"] = v
918        if file is not None:
919            query["file"] = file
920        return self._http.request_raw(f"/api/v1/solutions/{solution}/image", query=query)

Fetch a Solution cover image or gallery screenshot Returns the raw bytes of a Solution's cover image the bundled asset the Solution body's image: field names or, with the optional file param, one of its gallery screenshots (screenshots: entries). This endpoint backs the image_url and screenshot_urls fields of catalog payloads (such as GET /api/v1/solutions), which anonymous consumers the public marketplace's page cache, OpenGraph scrapers may hold far longer than a signed storage URL lives. Authorization is performed via a short, stable capability token rather than an HTTP header, so the URL never expires. The token is an HMAC-based capability tied to the Solution config ID. It does not expire, but the endpoint checks at fetch time that the Solution still declares the requested image without file, that it still declares a cover; with file, that the path is still among the body's declared image/screenshots so republishing without the asset (or hiding the Solution) turns the URL into a 404. Shared caches may continue serving the old image until the Cache-Control max-age of one hour elapses. v is an opaque cache key minted alongside the token; it changes when the Solution changes and is ignored by verification. All failure modes invalid config ID, invalid token, hidden Solution, no declared cover, a file outside the declared set, or an asset that doesn't resolve to a bundled image return a uniform 404 to avoid acting as an existence oracle.

Arguments:
  • solution: Solution config ID (cfg_...) whose cover image to fetch.
  • token: HMAC capability token authorizing access to this Solution's cover. Obtained from the image_url minted when the Solution was serialized.
  • v: Opaque cache key minted alongside the token; changes when the Solution changes. Ignored by token verification.
  • file: Bundled asset path of the image to serve (for example images/setup.png). Must be one of the paths the Solution body currently declares in image/screenshots anything else is a 404. When absent the declared cover (image:) is served.
Returns:

Raw cover image bytes, served with the asset's image content type.

def install( self, solution: str, input: SolutionInstallInput) -> archastro.platform.types.common.SolutionInstallResponse:
922    def install(self, solution: str, input: SolutionInstallInput) -> SolutionInstallResponse:
923        """
924        Install a Solution
925        Provisions a runtime resource from an already-imported Solution. The type of
926        resource created depends on the template the Solution wraps: an
927        `AgentTemplate` produces an Agent, an `AutomationTemplate` produces an
928        Automation, and attachment templates (`AgentRoutineTemplate`,
929        `AgentToolTemplate`, `AgentSkillTemplate`, `AgentComputerTemplate`) attach a
930        sub-resource to an existing Agent specified by `target`.
931        For Solutions that bundle more than one template, pass `template` (the ID or
932        `lookup_key` of the desired template) to select which one to provision.
933        Single-template Solutions do not require `template`.
934        Pairs with `POST /api/v1/solutions` (import): import puts the Solution into
935        the library; install provisions a runtime resource from it.
936
937        Args:
938            solution: Config ID (`cfg_...`) or `lookup_key` of the already-imported Solution to install from.
939            input: Request body.
940            input.acl: Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team.
941            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.
942            input.details: Template-specific install options selected by the `type` discriminator. AutomationTemplate installs accept `{type: "automation", prefills: ...}`.
943            input.install_inputs: Values applied to parameterized AgentTemplate prose during a root Agent install. Use `{values: {customer_label: "Acme"}}`.
944            input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's `agent_key`).
945            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.
946            input.name: Display name override for a provisioned Agent or Automation. Ignored for attachment Solutions.
947            input.org: Organization ID (`org_...`) for the install destination scope.
948            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.
949            input.team: Team ID (`team_...`) for the install destination scope.
950            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.
951            input.user: User ID (`usr_...`) for the install destination scope.
952
953        Returns:
954            The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID.
955        """
956        return self._http.request(
957            f"/api/v1/solutions/{solution}/install",
958            method="POST",
959            body=input,
960            response_type=SolutionInstallResponse,
961        )

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_...) or lookup_key of the already-imported Solution to install from.
  • input: Request body.
  • input.acl: Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team.
  • 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.
  • input.details: Template-specific install options selected by the type discriminator. AutomationTemplate installs accept {type: "automation", prefills: ...}.
  • input.install_inputs: Values applied to parameterized AgentTemplate prose during a root Agent install. Use {values: {customer_label: "Acme"}}.
  • 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 a provisioned Agent or Automation. Ignored for attachment Solutions.
  • input.org: Organization ID (org_...) for the install destination scope.
  • 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.
  • input.team: Team ID (team_...) for the install destination scope.
  • 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.
  • 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.

def readme( self, solution: str, token: str, *, file: str | None = None) -> dict[str, str]:
963    def readme(self, solution: str, token: str, *, file: str | None = None) -> dict[str, str]:
964        """
965        Retrieve a Solution README or asset
966        Serves the README markdown or a bundled asset for an imported Solution. Both
967        modes use the same path and require a short-lived signed `token` in the query
968        string rather than an `Authorization` header, so browsers can load asset URLs
969        directly from `<img src>` attributes without custom request logic.
970        When `file` is omitted the response is the Solution's `readme` field rendered
971        as `text/markdown`. All local asset references in the markdown are rewritten to
972        point back at this endpoint with `?file=PATH&token=TOKEN` so browsers can load
973        images inline without additional authentication.
974        When `file` is set the response is the raw bytes of the matching asset (a File
975        child whose `relative_path` equals `PATH`, or an inline `assets` entry by
976        name) with the asset's stored `Content-Type`.
977        Tokens are scoped to a single Solution, carry the viewer's app, org, and
978        sandbox context from the time they were minted, and expire after one hour.
979        Obtain a fresh token by calling `GET /api/v1/solutions/:solution`, which
980        always returns a newly minted `readme_url`.
981
982        Args:
983            solution: Solution config ID (`cfg_...`) identifying the Solution whose README or asset to retrieve.
984            token: Signed URL token minted by the list or show endpoint. Expires after one hour.
985            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.
986
987        Returns:
988            README markdown (`text/markdown`) when `file` is omitted, or the raw asset bytes with the asset's `Content-Type` when `file` is set.
989        """
990        query: dict[str, object] = {}
991        query["token"] = token
992        if file is not None:
993            query["file"] = file
994        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 real Content-Type; when absent the response is the README markdown.
Returns:

README markdown (text/markdown) when file is omitted, or the raw asset bytes with the asset's Content-Type when file is set.

def reconcile_product_knowledge( self, solution: str) -> archastro.platform.types.common.ProductKnowledgeReconcileResponse:
 996    def reconcile_product_knowledge(self, solution: str) -> ProductKnowledgeReconcileResponse:
 997        """
 998        Reconcile Customer Management product knowledge
 999        Recomputes every stamped customer's knowledge-search source allowlist
1000        from the template-resolved customer refs plus the install's current marked
1001        product-knowledge sources. Safe to retry; partial failures are returned by
1002        customer key rather than hidden behind an aggregate success.
1003
1004        Args:
1005            solution: Installed Customer Management Solution config (`cfg_...`) to reconcile.
1006
1007        Returns:
1008            Per-customer convergence result for this installed solution.
1009        """
1010        return self._http.request(
1011            f"/api/v1/solutions/{solution}/reconcile_product_knowledge",
1012            method="POST",
1013            response_type=ProductKnowledgeReconcileResponse,
1014        )

Reconcile Customer Management product knowledge Recomputes every stamped customer's knowledge-search source allowlist from the template-resolved customer refs plus the install's current marked product-knowledge sources. Safe to retry; partial failures are returned by customer key rather than hidden behind an aggregate success.

Arguments:
  • solution: Installed Customer Management Solution config (cfg_...) to reconcile.
Returns:

Per-customer convergence result for this installed solution.

def upgrade( self, solution: str, input: SolutionUpgradeInput) -> archastro.platform.types.common.SolutionUpgradeResponse:
1016    def upgrade(self, solution: str, input: SolutionUpgradeInput) -> SolutionUpgradeResponse:
1017        """
1018        Upgrade an installed Solution
1019        Applies an incoming bundle to an already-installed Solution in a single atomic
1020        transaction, bringing its configs in line with the new bundle. Config IDs are
1021        preserved across the upgrade. Configs that existed in the old bundle but are
1022        absent from the new one are orphaned (top-level) or hard-deleted (child rows).
1023        Two mutually exclusive source modes: pass `target_solution` to pull the
1024        incoming bundle from an existing Solution by ID or `lookup_key`, or pass
1025        `solution_bundle` to supply a complete inline bundle directly. Exactly one
1026        must be present.
1027        When `dry_run` is `true` the full diff is computed and returned but no
1028        changes are written. Pass the dry-run response's `review_fingerprint` as
1029        `expected_review_fingerprint` when applying to guard against the bundle
1030        changing between review and apply.
1031
1032        Args:
1033            solution: Config ID (`cfg_...`) or `lookup_key` of the currently installed Solution to upgrade.
1034            input: Request body.
1035            input.allow_downgrade: When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`.
1036            input.dry_run: When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`.
1037            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.
1038            input.org: Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this.
1039            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`.
1040            input.target_solution: Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`.
1041
1042        Returns:
1043            Updated Solution plus the full upgrade diff.
1044        """
1045        return self._http.request(
1046            f"/api/v1/solutions/{solution}/upgrade",
1047            method="POST",
1048            body=input,
1049            response_type=SolutionUpgradeResponse,
1050        )

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_...) or lookup_key of the currently installed Solution to upgrade.
  • input: Request body.
  • input.allow_downgrade: When true, permits an incoming solution_version lower than the currently installed version. Defaults to false.
  • input.dry_run: When true, computes and returns the full upgrade diff without persisting any changes. Defaults to false.
  • 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.
  • input.org: Organization ID (org_...) used to resolve org-scoped lookup_key values. 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_...) or lookup_key of the Solution to use as the incoming upgrade source. Mutually exclusive with solution_bundle.
Returns:

Updated Solution plus the full upgrade diff.

def view( self, solution: str, input: SolutionViewInput) -> None:
1052    def view(self, solution: str, input: SolutionViewInput) -> None:
1053        """
1054        Track a Solution detail-page view
1055        Records a `solution_viewed` analytics event for the identified Solution and
1056        returns `204 No Content`. Fired by the marketplace when a Solution's detail
1057        page is rendered in a browser, so publishers can see impressions alongside
1058        installs in their Solution analytics.
1059        Visibility matches `GET /api/v1/solutions/:solution`: unauthenticated callers
1060        (the logged-out marketplace) can only track Solutions published to the public
1061        catalog; anything the caller could not retrieve returns 404 and records
1062        nothing.
1063        Pass `anonymous` (the analytics visitor ID) so logged-out views can be
1064        counted as unique viewers. The event's Solution and publisher attribution are
1065        resolved server-side from the Solution row never from request input.
1066
1067        Args:
1068            solution: Solution config ID (`cfg_...`) or `lookup_key` of the viewed Solution.
1069            input: Request body.
1070            input.anonymous: Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the `POST /api/v1/t` events use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session.
1071
1072        Returns:
1073            Empty body. HTTP 204 indicates the Solution view was recorded successfully.
1074        """
1075        self._http.request(f"/api/v1/solutions/{solution}/view", method="POST", body=input)

Track a Solution detail-page view Records a solution_viewed analytics event for the identified Solution and returns 204 No Content. Fired by the marketplace when a Solution's detail page is rendered in a browser, so publishers can see impressions alongside installs in their Solution analytics. Visibility matches GET /api/v1/solutions/:solution: unauthenticated callers (the logged-out marketplace) can only track Solutions published to the public catalog; anything the caller could not retrieve returns 404 and records nothing. Pass anonymous (the analytics visitor ID) so logged-out views can be counted as unique viewers. The event's Solution and publisher attribution are resolved server-side from the Solution row never from request input.

Arguments:
  • solution: Solution config ID (cfg_...) or lookup_key of the viewed Solution.
  • input: Request body.
  • input.anonymous: Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the POST /api/v1/t events use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session.
Returns:

Empty body. HTTP 204 indicates the Solution view was recorded successfully.