archastro.platform.channels.api_chat_channel

   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: bc65fd615b6b
   4
   5from collections.abc import Callable
   6from datetime import datetime
   7from typing import TYPE_CHECKING, Any, Literal, Required, TypedDict
   8
   9if TYPE_CHECKING:
  10    from archastro.phx_channel.socket import Socket
  11
  12
  13class ApiChatForkThreadInput(TypedDict, total=False):
  14    "Fork a sub-thread from an existing message"
  15
  16    message_id: Required[str]
  17    title: str
  18
  19
  20class ApiChatMarkThreadReadInput(TypedDict):
  21    "Mark a thread as read up to a given message"
  22
  23    message_id: str
  24
  25
  26class ApiChatLoadMoreMessagesInput(TypedDict, total=False):
  27    "Load additional messages with cursor-based pagination"
  28
  29    after_cursor: str
  30    before_cursor: str
  31    include_metadata: bool
  32    limit: int
  33
  34
  35class ApiChatPostMessageInputContextItem(TypedDict, total=False):
  36    attributes: dict[str, Any] | None
  37    "Scalar key-value fields describing the context, such as route, repository, or pull-request number."
  38    content: str | None
  39    "Optional context body. The model receives it as escaped XML data, not a system instruction."
  40    title: str | None
  41    "Optional human-readable label for this context block."
  42    type: Required[str]
  43    "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores."
  44
  45
  46class ApiChatPostMessageInput(TypedDict, total=False):
  47    "Post a new message with optional uploads and reply-to"
  48
  49    content: Required[str]
  50    context: list[ApiChatPostMessageInputContextItem]
  51    idempotency_key: str
  52    mentions: list[str]
  53    metadata: dict[str, Any]
  54    reply_to: str
  55    uploads: list[dict[str, Any]]
  56
  57
  58class ApiChatPostSimpleMessageInputContextItem(TypedDict, total=False):
  59    attributes: dict[str, Any] | None
  60    "Scalar key-value fields describing the context, such as route, repository, or pull-request number."
  61    content: str | None
  62    "Optional context body. The model receives it as escaped XML data, not a system instruction."
  63    title: str | None
  64    "Optional human-readable label for this context block."
  65    type: Required[str]
  66    "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores."
  67
  68
  69class ApiChatPostSimpleMessageInput(TypedDict, total=False):
  70    "Post a simple text message"
  71
  72    content: str
  73    context: list[ApiChatPostSimpleMessageInputContextItem]
  74    idempotency_key: str
  75    mentions: list[str]
  76    metadata: dict[str, Any]
  77    reply_to: str
  78
  79
  80class ApiChatEditMessageInput(TypedDict):
  81    "Edit an existing message's content"
  82
  83    content: str
  84    message_id: str
  85
  86
  87class ApiChatDeleteMessageInput(TypedDict):
  88    "Delete a message"
  89
  90    message_id: str
  91
  92
  93class ApiChatAddReactionInput(TypedDict):
  94    "Add an emoji reaction to a message"
  95
  96    emoji: str
  97    message_id: str
  98
  99
 100class ApiChatRemoveReactionInput(TypedDict):
 101    "Remove an emoji reaction from a message"
 102
 103    emoji: str
 104    message_id: str
 105
 106
 107class ApiChatTypingInput(TypedDict):
 108    "Signal that the current user has started or stopped typing in the thread"
 109
 110    is_typing: bool
 111
 112
 113class ApiChatPublishLocalToolsInputLocalToolsItemFunction(TypedDict):
 114    description: str
 115    name: str
 116    parameters: dict[str, Any]
 117
 118
 119class ApiChatPublishLocalToolsInputLocalToolsItem(TypedDict):
 120    function: ApiChatPublishLocalToolsInputLocalToolsItemFunction
 121    "A function implemented by the client connected to a personal thread."
 122    type: Literal["function"]
 123
 124
 125class ApiChatPublishLocalToolsInput(TypedDict):
 126    "Replace the local tools advertised by this connection for later messages it posts"
 127
 128    local_tool_provider_id: str
 129    local_tools: list[ApiChatPublishLocalToolsInputLocalToolsItem]
 130
 131
 132class ApiChatLocalToolResultInput(TypedDict):
 133    "Return one complete result batch for a connection-local tool request"
 134
 135    agent_id: str
 136    generation: int
 137    provider_id: str
 138    request_id: str
 139    results: list[dict[str, Any] | dict[str, Any] | dict[str, Any]]
 140
 141
 142class MessageAddedPayloadMessageAclAddItem(TypedDict, total=False):
 143    actions: Required[list[str]]
 144    'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.'
 145    principal: str | None
 146    '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"`.'
 147    principal_type: Required[str]
 148    'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
 149
 150
 151class MessageAddedPayloadMessageAclGrantsItem(TypedDict, total=False):
 152    actions: Required[list[str]]
 153    'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.'
 154    principal: str | None
 155    '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"`.'
 156    principal_type: Required[str]
 157    'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
 158
 159
 160class MessageAddedPayloadMessageAclRemoveItem(TypedDict, total=False):
 161    principal: str | None
 162    '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"`.'
 163    principal_type: Required[str]
 164    'The kind of principal to remove. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
 165
 166
 167class MessageAddedPayloadMessageAcl(TypedDict, total=False):
 168    add: list[MessageAddedPayloadMessageAclAddItem] | None
 169    "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`."
 170    grants: list[MessageAddedPayloadMessageAclGrantsItem] | None
 171    "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`."
 172    remove: list[MessageAddedPayloadMessageAclRemoveItem] | None
 173    "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`."
 174
 175
 176class MessageAddedPayloadMessageActorsItemProfilePicture(TypedDict, total=False):
 177    file: str | None
 178    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
 179    height: int | None
 180    "Height of the image in pixels. `null` if not known."
 181    media: str | None
 182    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
 183    mime_type: str | None
 184    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
 185    refresh_url: str | None
 186    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
 187    url: str | None
 188    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
 189    width: int | None
 190    "Width of the image in pixels. `null` if not known."
 191
 192
 193class MessageAddedPayloadMessageActorsItem(TypedDict, total=False):
 194    alias: str | None
 195    "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured."
 196    id: str | None
 197    'Composite actor identifier. Format is `"user-<usr_...>"` for human users or `"agent-<agi_...>"` for agents.'
 198    name: str | None
 199    "Display name of the actor shown in the UI. `null` if no name is set."
 200    profile_picture: MessageAddedPayloadMessageActorsItemProfilePicture | None
 201    "Profile picture for the actor. `null` if the actor has no profile picture."
 202
 203
 204class MessageAddedPayloadMessageAttachmentsItemImageSource(TypedDict, total=False):
 205    file: str | None
 206    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
 207    height: int | None
 208    "Height of the image in pixels. `null` if not known."
 209    media: str | None
 210    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
 211    mime_type: str | None
 212    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
 213    refresh_url: str | None
 214    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
 215    url: str | None
 216    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
 217    width: int | None
 218    "Width of the image in pixels. `null` if not known."
 219
 220
 221class MessageAddedPayloadMessageAttachmentsItemVariantsItemImageSource(TypedDict, total=False):
 222    file: str | None
 223    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
 224    height: int | None
 225    "Height of the image in pixels. `null` if not known."
 226    media: str | None
 227    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
 228    mime_type: str | None
 229    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
 230    refresh_url: str | None
 231    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
 232    url: str | None
 233    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
 234    width: int | None
 235    "Width of the image in pixels. `null` if not known."
 236
 237
 238class MessageAddedPayloadMessageAttachmentsItemVariantsItem(TypedDict, total=False):
 239    byte_size: int | None
 240    "Size of the stored bytes."
 241    content_type: str | None
 242    'MIME type of this variant\'s file (e.g., `"image/jpeg"`, `"video/mp4"`). `null` if the file is not loaded.'
 243    created_at: datetime | None
 244    "When this variant was created (ISO 8601)."
 245    duration_ms: int | None
 246    "Client-declared video duration in milliseconds; not server-probed."
 247    file: str | None
 248    "ID of the underlying storage file that backs this variant (`fil_...`)."
 249    filename: str | None
 250    "Original filename of the uploaded file for this variant. `null` if the file is not loaded."
 251    height: int | None
 252    "Height of this variant in pixels. `null` if not recorded."
 253    id: Required[str]
 254    "Media variant ID (`mvr_...`)."
 255    image_source: MessageAddedPayloadMessageAttachmentsItemVariantsItemImageSource | None
 256    "Resolved image delivery metadata for this variant, including dimensions and CDN URL. `null` for non-image content types."
 257    sha256: str | None
 258    "Lowercase SHA-256 of the stored bytes."
 259    updated_at: datetime | None
 260    "When this variant was last updated (ISO 8601)."
 261    url: str | None
 262    "Signed download URL for this variant, resolved at request time. `null` if the file is unavailable."
 263    url_expires_at: datetime | None
 264    "Expiry of this variant's playable URL."
 265    variant_key: str | None
 266    'Identifier for this variant\'s processing tier. Common values include `"original"` (the unmodified upload) and `"thumbnail"` (a resized preview).'
 267    width: int | None
 268    "Width of this variant in pixels. `null` if not recorded."
 269
 270
 271class MessageAddedPayloadMessageAttachmentsItem(TypedDict, total=False):
 272    content_type: str | None
 273    'MIME type of the attached file, e.g. `"image/png"` or `"application/pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.'
 274    description: str | None
 275    "Short description. The page meta-description for `scraped_link`, the artifact description for `artifact`, and the task description for `task` types. `null` on other types."
 276    filename: str | None
 277    'Original filename of the attached file, e.g. `"report.pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.'
 278    height: int | None
 279    "Height in pixels of the media item. Present on `media` type only. `null` otherwise."
 280    id: Required[str]
 281    "Unique identifier for this attachment within the message."
 282    image_height: int | None
 283    "Height in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise."
 284    image_source: MessageAddedPayloadMessageAttachmentsItemImageSource | None
 285    "Image source metadata for inline rendering. Present on `file`, `scraped_link`, `artifact`, and `media` types when the content is an image. `null` otherwise."
 286    image_url: str | None
 287    "URL of the preview image extracted from the scraped page. Present on `scraped_link` type only. `null` otherwise."
 288    image_width: int | None
 289    "Width in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise."
 290    key: str | None
 291    'For `structured_data` type, what the data is about as set by the sender (for example `"pr:14963"`), copied from the record so it can be read without `object`. Not unique. `null` when unset and on other types.'
 292    media_type: str | None
 293    'The media category, e.g. `"video"` or `"audio"`. Present on `media` type only; omitted otherwise.'
 294    name: str | None
 295    "Display name of the media item. Present on `media` type only. `null` otherwise."
 296    object: dict[str, Any] | None
 297    "The full embedded object payload. For `task` type, contains the task record. For `action` type, contains the action definition. For `chart` type, contains the chart with its inline `spec`. For `structured_data` type, contains the inline `data` and the `schema_url` or `schema_config` naming its JSON Schema. Omitted on other types."
 298    title: str | None
 299    "Display title. The page title for `scraped_link`, the artifact name for `artifact`, and the task title for `task` types. `null` on other types."
 300    type: Required[str]
 301    'The attachment type. One of `"file"`, `"scraped_link"`, `"artifact"`, `"task"`, `"media"`, `"action"`, `"chart"`, or `"structured_data"`. Determines which additional fields are present.'
 302    url: str | None
 303    "URL to access the resource. A signed download URL for `file` and `artifact` types; the original URL for `scraped_link`; a media playback URL for `media`. `null` on `task` and `action` types."
 304    variants: list[MessageAddedPayloadMessageAttachmentsItemVariantsItem] | None
 305    "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise."
 306    version: int | None
 307    "Version number of the attached artifact at the time of attachment. Present on `artifact` type only. `null` otherwise."
 308    width: int | None
 309    "Width in pixels of the media item. Present on `media` type only. `null` otherwise."
 310
 311
 312class MessageAddedPayloadMessageContextItem(TypedDict, total=False):
 313    attributes: dict[str, Any] | None
 314    "Scalar key-value fields describing the context, such as route, repository, or pull-request number."
 315    content: str | None
 316    "Optional context body. The model receives it as escaped XML data, not a system instruction."
 317    title: str | None
 318    "Optional human-readable label for this context block."
 319    type: Required[str]
 320    "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores."
 321
 322
 323class MessageAddedPayloadMessageReactionsItem(TypedDict, total=False):
 324    payload: dict[str, Any] | None
 325    'Type-specific reaction data. For `"emoji_reaction"` reactions, contains an `emoji` key with the Unicode emoji string (e.g., `" "`).'
 326    type: Required[str]
 327    'Reaction type identifier. Currently always `"emoji_reaction"` for emoji-based reactions.'
 328    user: str | None
 329    "Public ID of the user who added the reaction (`usr_...`)."
 330
 331
 332class MessageAddedPayloadMessage(TypedDict, total=False):
 333    acl: MessageAddedPayloadMessageAcl | None
 334    "Access control list for private messages (grants with `read` action). Only returned to resource owners (and privileged/org-admin viewers) via server-side `field_redactions: [acl: :owner]`; `null` for everyone else."
 335    actors: list[MessageAddedPayloadMessageActorsItem] | None
 336    "Resolved actor descriptors for the message sender, combining identity and display metadata. Always contains exactly one entry."
 337    agent: str | None
 338    "ID of the agent user that sent this message (`agi_...`). `null` for messages sent by human users."
 339    agent_mode: Literal["cli", "embedded"] | None
 340    "Local agent execution mode for this message. One of `cli`, `embedded`, or `null` when the message was not created by a local agent execution path."
 341    attachments: list[MessageAddedPayloadMessageAttachmentsItem] | None
 342    "Files, links, tasks, media, artifacts, and actions attached to this message. Empty array if there are no attachments."
 343    branched_thread: str | None
 344    "ID of the thread that was branched from this message (`thr_...`). `null` if this message has not spawned a branch thread."
 345    content: str | None
 346    "Text content of the message. `null` for messages that contain only attachments."
 347    context: list[MessageAddedPayloadMessageContextItem] | None
 348    "Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions."
 349    created_at: str | None
 350    "When the message was posted (ISO 8601)."
 351    has_replies: bool | None
 352    "Whether this message has at least one reply. Only present when explicitly requested or computed by the server."
 353    id: Required[str]
 354    "Message ID (`msg_...`)."
 355    idempotency_key: str | None
 356    "Client-supplied idempotency key used to deduplicate message sends. `null` if the sender did not provide one."
 357    is_deleted: bool | None
 358    "Whether this message is a deletion tombstone. `true` only on the `message_updated` broadcast emitted when a message is deleted: the original content is replaced with a placeholder and the message no longer exists on the server. Always `false` for live messages."
 359    legacy_agent: str | None
 360    "Identifier of the legacy chat agent that sent this message, if applicable. `null` for messages sent by users or modern agent users."
 361    metadata: dict[str, Any] | None
 362    "Arbitrary key-value metadata attached to the message. Always present; defaults to an empty object when no metadata has been set."
 363    org: str | None
 364    "ID of the organization that owns this message (`org_...`)."
 365    reactions: list[MessageAddedPayloadMessageReactionsItem] | None
 366    "Emoji and other reactions added to this message by users. Empty array if no reactions have been added or the association is not preloaded."
 367    rendering_mode: str | None
 368    'Display hint for how the message should be rendered. One of `"reply"`, `"direct"`, or `"inline"`. `null` for user-authored messages, which are always rendered as standard replies.'
 369    replies: list[dict[str, Any]] | None
 370    "Inline array of reply messages, each serialized as a full message object. Only present when the server has preloaded replies for this message."
 371    replies_after_cursor: str | None
 372    "Opaque pagination cursor to fetch replies posted after the current page. Only present when inline replies are included in the response."
 373    replies_before_cursor: str | None
 374    "Opaque pagination cursor to fetch replies posted before the current page. Only present when inline replies are included in the response."
 375    reply_count: int | None
 376    "Total number of direct replies to this message. Only present when explicitly requested or computed by the server."
 377    reply_to: dict[str, Any] | None
 378    "The parent message this message is a reply to, expanded as a full message object when loaded. `null` if this is a top-level message or the association is not preloaded."
 379    root_message_id: str | None
 380    "ID of the root message in this reply chain (`msg_...`). `null` for a top-level message. The value is persisted when the reply is created, so callers can correlate a multi-turn session without walking parent messages."
 381    sandbox: str | None
 382    "ID of the developer sandbox this message belongs to (`dsb_...`). `null` for non-sandbox messages."
 383    team: str | None
 384    "ID of the team this message is scoped to (`tem_...`). `null` if the message is not team-scoped."
 385    thread: str | None
 386    "ID of the thread this message belongs to (`thr_...`)."
 387    type: str | None
 388    "Optional client-defined classification for the message (for example `note` or `status`). Free-form string up to 64 characters. The value `system` is reserved for platform-authored messages and cannot be set by clients. `null` when unset."
 389    user: str | dict[str, Any] | None
 390    "The human user who sent this message. Returns a public ID string (`usr_...`) when the association is not preloaded, or an expanded user object when it is. `null` for messages sent by agents."
 391    visibility: Literal["default", "private"] | None
 392    "Message-level visibility. `default` is visible to anyone who can see the parent thread. `private` is restricted to the sender and explicit ACL `read` grantees."
 393
 394
 395class MessageAddedPayload(TypedDict, total=False):
 396    "Broadcast when a new message is added to a thread"
 397
 398    after_cursor: str | None
 399    before_cursor: str | None
 400    message: MessageAddedPayloadMessage | None
 401    "A chat message posted in a thread, including its content, author, attachments, reactions, and optional reply metadata."
 402    thread_id: str | None
 403
 404
 405class MessageUpdatedPayloadMessageAclAddItem(TypedDict, total=False):
 406    actions: Required[list[str]]
 407    'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.'
 408    principal: str | None
 409    '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"`.'
 410    principal_type: Required[str]
 411    'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
 412
 413
 414class MessageUpdatedPayloadMessageAclGrantsItem(TypedDict, total=False):
 415    actions: Required[list[str]]
 416    'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.'
 417    principal: str | None
 418    '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"`.'
 419    principal_type: Required[str]
 420    'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
 421
 422
 423class MessageUpdatedPayloadMessageAclRemoveItem(TypedDict, total=False):
 424    principal: str | None
 425    '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"`.'
 426    principal_type: Required[str]
 427    'The kind of principal to remove. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
 428
 429
 430class MessageUpdatedPayloadMessageAcl(TypedDict, total=False):
 431    add: list[MessageUpdatedPayloadMessageAclAddItem] | None
 432    "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`."
 433    grants: list[MessageUpdatedPayloadMessageAclGrantsItem] | None
 434    "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`."
 435    remove: list[MessageUpdatedPayloadMessageAclRemoveItem] | None
 436    "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`."
 437
 438
 439class MessageUpdatedPayloadMessageActorsItemProfilePicture(TypedDict, total=False):
 440    file: str | None
 441    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
 442    height: int | None
 443    "Height of the image in pixels. `null` if not known."
 444    media: str | None
 445    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
 446    mime_type: str | None
 447    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
 448    refresh_url: str | None
 449    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
 450    url: str | None
 451    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
 452    width: int | None
 453    "Width of the image in pixels. `null` if not known."
 454
 455
 456class MessageUpdatedPayloadMessageActorsItem(TypedDict, total=False):
 457    alias: str | None
 458    "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured."
 459    id: str | None
 460    'Composite actor identifier. Format is `"user-<usr_...>"` for human users or `"agent-<agi_...>"` for agents.'
 461    name: str | None
 462    "Display name of the actor shown in the UI. `null` if no name is set."
 463    profile_picture: MessageUpdatedPayloadMessageActorsItemProfilePicture | None
 464    "Profile picture for the actor. `null` if the actor has no profile picture."
 465
 466
 467class MessageUpdatedPayloadMessageAttachmentsItemImageSource(TypedDict, total=False):
 468    file: str | None
 469    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
 470    height: int | None
 471    "Height of the image in pixels. `null` if not known."
 472    media: str | None
 473    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
 474    mime_type: str | None
 475    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
 476    refresh_url: str | None
 477    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
 478    url: str | None
 479    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
 480    width: int | None
 481    "Width of the image in pixels. `null` if not known."
 482
 483
 484class MessageUpdatedPayloadMessageAttachmentsItemVariantsItemImageSource(TypedDict, total=False):
 485    file: str | None
 486    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
 487    height: int | None
 488    "Height of the image in pixels. `null` if not known."
 489    media: str | None
 490    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
 491    mime_type: str | None
 492    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
 493    refresh_url: str | None
 494    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
 495    url: str | None
 496    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
 497    width: int | None
 498    "Width of the image in pixels. `null` if not known."
 499
 500
 501class MessageUpdatedPayloadMessageAttachmentsItemVariantsItem(TypedDict, total=False):
 502    byte_size: int | None
 503    "Size of the stored bytes."
 504    content_type: str | None
 505    'MIME type of this variant\'s file (e.g., `"image/jpeg"`, `"video/mp4"`). `null` if the file is not loaded.'
 506    created_at: datetime | None
 507    "When this variant was created (ISO 8601)."
 508    duration_ms: int | None
 509    "Client-declared video duration in milliseconds; not server-probed."
 510    file: str | None
 511    "ID of the underlying storage file that backs this variant (`fil_...`)."
 512    filename: str | None
 513    "Original filename of the uploaded file for this variant. `null` if the file is not loaded."
 514    height: int | None
 515    "Height of this variant in pixels. `null` if not recorded."
 516    id: Required[str]
 517    "Media variant ID (`mvr_...`)."
 518    image_source: MessageUpdatedPayloadMessageAttachmentsItemVariantsItemImageSource | None
 519    "Resolved image delivery metadata for this variant, including dimensions and CDN URL. `null` for non-image content types."
 520    sha256: str | None
 521    "Lowercase SHA-256 of the stored bytes."
 522    updated_at: datetime | None
 523    "When this variant was last updated (ISO 8601)."
 524    url: str | None
 525    "Signed download URL for this variant, resolved at request time. `null` if the file is unavailable."
 526    url_expires_at: datetime | None
 527    "Expiry of this variant's playable URL."
 528    variant_key: str | None
 529    'Identifier for this variant\'s processing tier. Common values include `"original"` (the unmodified upload) and `"thumbnail"` (a resized preview).'
 530    width: int | None
 531    "Width of this variant in pixels. `null` if not recorded."
 532
 533
 534class MessageUpdatedPayloadMessageAttachmentsItem(TypedDict, total=False):
 535    content_type: str | None
 536    'MIME type of the attached file, e.g. `"image/png"` or `"application/pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.'
 537    description: str | None
 538    "Short description. The page meta-description for `scraped_link`, the artifact description for `artifact`, and the task description for `task` types. `null` on other types."
 539    filename: str | None
 540    'Original filename of the attached file, e.g. `"report.pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.'
 541    height: int | None
 542    "Height in pixels of the media item. Present on `media` type only. `null` otherwise."
 543    id: Required[str]
 544    "Unique identifier for this attachment within the message."
 545    image_height: int | None
 546    "Height in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise."
 547    image_source: MessageUpdatedPayloadMessageAttachmentsItemImageSource | None
 548    "Image source metadata for inline rendering. Present on `file`, `scraped_link`, `artifact`, and `media` types when the content is an image. `null` otherwise."
 549    image_url: str | None
 550    "URL of the preview image extracted from the scraped page. Present on `scraped_link` type only. `null` otherwise."
 551    image_width: int | None
 552    "Width in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise."
 553    key: str | None
 554    'For `structured_data` type, what the data is about as set by the sender (for example `"pr:14963"`), copied from the record so it can be read without `object`. Not unique. `null` when unset and on other types.'
 555    media_type: str | None
 556    'The media category, e.g. `"video"` or `"audio"`. Present on `media` type only; omitted otherwise.'
 557    name: str | None
 558    "Display name of the media item. Present on `media` type only. `null` otherwise."
 559    object: dict[str, Any] | None
 560    "The full embedded object payload. For `task` type, contains the task record. For `action` type, contains the action definition. For `chart` type, contains the chart with its inline `spec`. For `structured_data` type, contains the inline `data` and the `schema_url` or `schema_config` naming its JSON Schema. Omitted on other types."
 561    title: str | None
 562    "Display title. The page title for `scraped_link`, the artifact name for `artifact`, and the task title for `task` types. `null` on other types."
 563    type: Required[str]
 564    'The attachment type. One of `"file"`, `"scraped_link"`, `"artifact"`, `"task"`, `"media"`, `"action"`, `"chart"`, or `"structured_data"`. Determines which additional fields are present.'
 565    url: str | None
 566    "URL to access the resource. A signed download URL for `file` and `artifact` types; the original URL for `scraped_link`; a media playback URL for `media`. `null` on `task` and `action` types."
 567    variants: list[MessageUpdatedPayloadMessageAttachmentsItemVariantsItem] | None
 568    "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise."
 569    version: int | None
 570    "Version number of the attached artifact at the time of attachment. Present on `artifact` type only. `null` otherwise."
 571    width: int | None
 572    "Width in pixels of the media item. Present on `media` type only. `null` otherwise."
 573
 574
 575class MessageUpdatedPayloadMessageContextItem(TypedDict, total=False):
 576    attributes: dict[str, Any] | None
 577    "Scalar key-value fields describing the context, such as route, repository, or pull-request number."
 578    content: str | None
 579    "Optional context body. The model receives it as escaped XML data, not a system instruction."
 580    title: str | None
 581    "Optional human-readable label for this context block."
 582    type: Required[str]
 583    "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores."
 584
 585
 586class MessageUpdatedPayloadMessageReactionsItem(TypedDict, total=False):
 587    payload: dict[str, Any] | None
 588    'Type-specific reaction data. For `"emoji_reaction"` reactions, contains an `emoji` key with the Unicode emoji string (e.g., `" "`).'
 589    type: Required[str]
 590    'Reaction type identifier. Currently always `"emoji_reaction"` for emoji-based reactions.'
 591    user: str | None
 592    "Public ID of the user who added the reaction (`usr_...`)."
 593
 594
 595class MessageUpdatedPayloadMessage(TypedDict, total=False):
 596    acl: MessageUpdatedPayloadMessageAcl | None
 597    "Access control list for private messages (grants with `read` action). Only returned to resource owners (and privileged/org-admin viewers) via server-side `field_redactions: [acl: :owner]`; `null` for everyone else."
 598    actors: list[MessageUpdatedPayloadMessageActorsItem] | None
 599    "Resolved actor descriptors for the message sender, combining identity and display metadata. Always contains exactly one entry."
 600    agent: str | None
 601    "ID of the agent user that sent this message (`agi_...`). `null` for messages sent by human users."
 602    agent_mode: Literal["cli", "embedded"] | None
 603    "Local agent execution mode for this message. One of `cli`, `embedded`, or `null` when the message was not created by a local agent execution path."
 604    attachments: list[MessageUpdatedPayloadMessageAttachmentsItem] | None
 605    "Files, links, tasks, media, artifacts, and actions attached to this message. Empty array if there are no attachments."
 606    branched_thread: str | None
 607    "ID of the thread that was branched from this message (`thr_...`). `null` if this message has not spawned a branch thread."
 608    content: str | None
 609    "Text content of the message. `null` for messages that contain only attachments."
 610    context: list[MessageUpdatedPayloadMessageContextItem] | None
 611    "Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions."
 612    created_at: str | None
 613    "When the message was posted (ISO 8601)."
 614    has_replies: bool | None
 615    "Whether this message has at least one reply. Only present when explicitly requested or computed by the server."
 616    id: Required[str]
 617    "Message ID (`msg_...`)."
 618    idempotency_key: str | None
 619    "Client-supplied idempotency key used to deduplicate message sends. `null` if the sender did not provide one."
 620    is_deleted: bool | None
 621    "Whether this message is a deletion tombstone. `true` only on the `message_updated` broadcast emitted when a message is deleted: the original content is replaced with a placeholder and the message no longer exists on the server. Always `false` for live messages."
 622    legacy_agent: str | None
 623    "Identifier of the legacy chat agent that sent this message, if applicable. `null` for messages sent by users or modern agent users."
 624    metadata: dict[str, Any] | None
 625    "Arbitrary key-value metadata attached to the message. Always present; defaults to an empty object when no metadata has been set."
 626    org: str | None
 627    "ID of the organization that owns this message (`org_...`)."
 628    reactions: list[MessageUpdatedPayloadMessageReactionsItem] | None
 629    "Emoji and other reactions added to this message by users. Empty array if no reactions have been added or the association is not preloaded."
 630    rendering_mode: str | None
 631    'Display hint for how the message should be rendered. One of `"reply"`, `"direct"`, or `"inline"`. `null` for user-authored messages, which are always rendered as standard replies.'
 632    replies: list[dict[str, Any]] | None
 633    "Inline array of reply messages, each serialized as a full message object. Only present when the server has preloaded replies for this message."
 634    replies_after_cursor: str | None
 635    "Opaque pagination cursor to fetch replies posted after the current page. Only present when inline replies are included in the response."
 636    replies_before_cursor: str | None
 637    "Opaque pagination cursor to fetch replies posted before the current page. Only present when inline replies are included in the response."
 638    reply_count: int | None
 639    "Total number of direct replies to this message. Only present when explicitly requested or computed by the server."
 640    reply_to: dict[str, Any] | None
 641    "The parent message this message is a reply to, expanded as a full message object when loaded. `null` if this is a top-level message or the association is not preloaded."
 642    root_message_id: str | None
 643    "ID of the root message in this reply chain (`msg_...`). `null` for a top-level message. The value is persisted when the reply is created, so callers can correlate a multi-turn session without walking parent messages."
 644    sandbox: str | None
 645    "ID of the developer sandbox this message belongs to (`dsb_...`). `null` for non-sandbox messages."
 646    team: str | None
 647    "ID of the team this message is scoped to (`tem_...`). `null` if the message is not team-scoped."
 648    thread: str | None
 649    "ID of the thread this message belongs to (`thr_...`)."
 650    type: str | None
 651    "Optional client-defined classification for the message (for example `note` or `status`). Free-form string up to 64 characters. The value `system` is reserved for platform-authored messages and cannot be set by clients. `null` when unset."
 652    user: str | dict[str, Any] | None
 653    "The human user who sent this message. Returns a public ID string (`usr_...`) when the association is not preloaded, or an expanded user object when it is. `null` for messages sent by agents."
 654    visibility: Literal["default", "private"] | None
 655    "Message-level visibility. `default` is visible to anyone who can see the parent thread. `private` is restricted to the sender and explicit ACL `read` grantees."
 656
 657
 658class MessageUpdatedPayload(TypedDict, total=False):
 659    "Broadcast when a message is updated or removed"
 660
 661    message: MessageUpdatedPayloadMessage | None
 662    "A chat message posted in a thread, including its content, author, attachments, reactions, and optional reply metadata."
 663    thread_id: str | None
 664
 665
 666class ThreadEventPayload(TypedDict, total=False):
 667    "Broadcast thread-level events (agent updates, read state, unread counts)"
 668
 669    payload: dict[str, Any] | None
 670    thread_id: str | None
 671    type: str | None
 672
 673
 674class SystemEventPayload(TypedDict, total=False):
 675    "Broadcast system-wide events"
 676
 677    event: dict[str, Any] | None
 678
 679
 680class TypingPayloadActorProfilePicture(TypedDict, total=False):
 681    file: str | None
 682    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
 683    height: int | None
 684    "Height of the image in pixels. `null` if not known."
 685    media: str | None
 686    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
 687    mime_type: str | None
 688    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
 689    refresh_url: str | None
 690    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
 691    url: str | None
 692    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
 693    width: int | None
 694    "Width of the image in pixels. `null` if not known."
 695
 696
 697class TypingPayloadActor(TypedDict, total=False):
 698    alias: str | None
 699    "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured."
 700    id: str | None
 701    'Composite actor identifier. Format is `"user-<usr_...>"` for human users or `"agent-<agi_...>"` for agents.'
 702    name: str | None
 703    "Display name of the actor shown in the UI. `null` if no name is set."
 704    profile_picture: TypingPayloadActorProfilePicture | None
 705    "Profile picture for the actor. `null` if the actor has no profile picture."
 706
 707
 708class TypingPayload(TypedDict, total=False):
 709    "Broadcast when a participant (human or agent) starts or stops typing. Ephemeral; never persisted."
 710
 711    actor: TypingPayloadActor | None
 712    "The entity that authored a message, either a human user or an agent."
 713    is_typing: bool | None
 714    thread_id: str | None
 715
 716
 717class LocalToolCallPayloadRequestCallsItem(TypedDict):
 718    arguments: dict[str, Any]
 719    id: str
 720    name: str
 721
 722
 723class LocalToolCallPayloadRequest(TypedDict):
 724    calls: list[LocalToolCallPayloadRequestCallsItem]
 725    id: str
 726
 727
 728class LocalToolCallPayload(TypedDict, total=False):
 729    "Invoke local functions on the exact connection that published them"
 730
 731    agent_id: str | None
 732    expires_at: datetime | None
 733    generation: int | None
 734    provider_id: str | None
 735    request: LocalToolCallPayloadRequest | None
 736    "A correlated batch of local function calls targeted to one connection."
 737    thread_id: str | None
 738
 739
 740class LocalToolCancelledPayload(TypedDict, total=False):
 741    "Stop local work because the server no longer accepts its result"
 742
 743    generation: int | None
 744    provider_id: str | None
 745    reason: str | None
 746    request_id: str | None
 747
 748
 749# Channel for real-time chat messaging.
 750# Supports team-scoped and user-scoped threads with keyed, transient, and direct
 751# thread access patterns, plus org-scoped joins for organization-visibility
 752# threads, where org membership alone grants read and post.
 753class ApiChatChannel:
 754    def __init__(self, channel, join_response=None):
 755        self._channel = channel
 756        self.join_response = join_response
 757
 758    # Join a team-scoped thread by ID
 759    @staticmethod
 760    def topic_team_thread(team_id: str, thread_id: str) -> str:
 761        return f"api:chat:team:{team_id}:thread:{thread_id}"
 762
 763    # Join a team-scoped thread by ID
 764    @classmethod
 765    async def join_team_thread(
 766        cls,
 767        socket: "Socket",
 768        team_id: str,
 769        thread_id: str,
 770        *,
 771        after_cursor: str | None = None,
 772        before_cursor: str | None = None,
 773        include_metadata: bool | None = None,
 774        limit: int | None = None,
 775    ) -> "ApiChatChannel":
 776        topic = cls.topic_team_thread(team_id, thread_id)
 777        channel = socket.channel(topic)
 778        payload: dict[str, object] = {}
 779        if after_cursor is not None:
 780            payload["after_cursor"] = after_cursor
 781        if before_cursor is not None:
 782            payload["before_cursor"] = before_cursor
 783        if include_metadata is not None:
 784            payload["include_metadata"] = include_metadata
 785        if limit is not None:
 786            payload["limit"] = limit
 787        join_response = await channel.join(payload)
 788        return cls(channel, join_response)
 789
 790    # Join or create a team-scoped keyed thread
 791    @staticmethod
 792    def topic_team_keyed(team_id: str, key: str) -> str:
 793        return f"api:chat:team:{team_id}:key:{key}"
 794
 795    # Join or create a team-scoped keyed thread
 796    @classmethod
 797    async def join_team_keyed(
 798        cls,
 799        socket: "Socket",
 800        team_id: str,
 801        key: str,
 802        *,
 803        after_cursor: str | None = None,
 804        before_cursor: str | None = None,
 805        include_metadata: bool | None = None,
 806        limit: int | None = None,
 807    ) -> "ApiChatChannel":
 808        topic = cls.topic_team_keyed(team_id, key)
 809        channel = socket.channel(topic)
 810        payload: dict[str, object] = {}
 811        if after_cursor is not None:
 812            payload["after_cursor"] = after_cursor
 813        if before_cursor is not None:
 814            payload["before_cursor"] = before_cursor
 815        if include_metadata is not None:
 816            payload["include_metadata"] = include_metadata
 817        if limit is not None:
 818            payload["limit"] = limit
 819        join_response = await channel.join(payload)
 820        return cls(channel, join_response)
 821
 822    # Join a team-scoped transient (ephemeral) thread
 823    @staticmethod
 824    def topic_team_transient(team_id: str, key: str) -> str:
 825        return f"api:chat:team:{team_id}:transient:{key}"
 826
 827    # Join a team-scoped transient (ephemeral) thread
 828    @classmethod
 829    async def join_team_transient(
 830        cls,
 831        socket: "Socket",
 832        team_id: str,
 833        key: str,
 834        *,
 835        after_cursor: str | None = None,
 836        before_cursor: str | None = None,
 837        include_metadata: bool | None = None,
 838        limit: int | None = None,
 839    ) -> "ApiChatChannel":
 840        topic = cls.topic_team_transient(team_id, key)
 841        channel = socket.channel(topic)
 842        payload: dict[str, object] = {}
 843        if after_cursor is not None:
 844            payload["after_cursor"] = after_cursor
 845        if before_cursor is not None:
 846            payload["before_cursor"] = before_cursor
 847        if include_metadata is not None:
 848            payload["include_metadata"] = include_metadata
 849        if limit is not None:
 850            payload["limit"] = limit
 851        join_response = await channel.join(payload)
 852        return cls(channel, join_response)
 853
 854    # Join an organization-visibility thread by ID
 855    @staticmethod
 856    def topic_org_thread(org_id: str, thread_id: str) -> str:
 857        return f"api:chat:org:{org_id}:thread:{thread_id}"
 858
 859    # Join an organization-visibility thread by ID
 860    @classmethod
 861    async def join_org_thread(
 862        cls,
 863        socket: "Socket",
 864        org_id: str,
 865        thread_id: str,
 866        *,
 867        after_cursor: str | None = None,
 868        before_cursor: str | None = None,
 869        include_metadata: bool | None = None,
 870        limit: int | None = None,
 871    ) -> "ApiChatChannel":
 872        topic = cls.topic_org_thread(org_id, thread_id)
 873        channel = socket.channel(topic)
 874        payload: dict[str, object] = {}
 875        if after_cursor is not None:
 876            payload["after_cursor"] = after_cursor
 877        if before_cursor is not None:
 878            payload["before_cursor"] = before_cursor
 879        if include_metadata is not None:
 880            payload["include_metadata"] = include_metadata
 881        if limit is not None:
 882            payload["limit"] = limit
 883        join_response = await channel.join(payload)
 884        return cls(channel, join_response)
 885
 886    # Join a user-scoped thread by ID, optionally supplying local tools for a personal thread
 887    @staticmethod
 888    def topic_user_thread(thread_id: str) -> str:
 889        return f"api:chat:user:thread:{thread_id}"
 890
 891    # Join a user-scoped thread by ID, optionally supplying local tools for a personal thread
 892    @classmethod
 893    async def join_user_thread(
 894        cls,
 895        socket: "Socket",
 896        thread_id: str,
 897        *,
 898        after_cursor: str | None = None,
 899        before_cursor: str | None = None,
 900        include_metadata: bool | None = None,
 901        limit: int | None = None,
 902        local_tool_provider_id: str | None = None,
 903        local_tools: list[dict[str, Any]] | None = None,
 904    ) -> "ApiChatChannel":
 905        topic = cls.topic_user_thread(thread_id)
 906        channel = socket.channel(topic)
 907        payload: dict[str, object] = {}
 908        if after_cursor is not None:
 909            payload["after_cursor"] = after_cursor
 910        if before_cursor is not None:
 911            payload["before_cursor"] = before_cursor
 912        if include_metadata is not None:
 913            payload["include_metadata"] = include_metadata
 914        if limit is not None:
 915            payload["limit"] = limit
 916        if local_tool_provider_id is not None:
 917            payload["local_tool_provider_id"] = local_tool_provider_id
 918        if local_tools is not None:
 919            payload["local_tools"] = local_tools
 920        join_response = await channel.join(payload)
 921        return cls(channel, join_response)
 922
 923    # Join or create a user-scoped keyed thread
 924    @staticmethod
 925    def topic_user_keyed(key: str) -> str:
 926        return f"api:chat:user:key:{key}"
 927
 928    # Join or create a user-scoped keyed thread
 929    @classmethod
 930    async def join_user_keyed(
 931        cls,
 932        socket: "Socket",
 933        key: str,
 934        *,
 935        after_cursor: str | None = None,
 936        before_cursor: str | None = None,
 937        include_metadata: bool | None = None,
 938        limit: int | None = None,
 939    ) -> "ApiChatChannel":
 940        topic = cls.topic_user_keyed(key)
 941        channel = socket.channel(topic)
 942        payload: dict[str, object] = {}
 943        if after_cursor is not None:
 944            payload["after_cursor"] = after_cursor
 945        if before_cursor is not None:
 946            payload["before_cursor"] = before_cursor
 947        if include_metadata is not None:
 948            payload["include_metadata"] = include_metadata
 949        if limit is not None:
 950            payload["limit"] = limit
 951        join_response = await channel.join(payload)
 952        return cls(channel, join_response)
 953
 954    # Join a user-scoped transient (ephemeral) thread
 955    @staticmethod
 956    def topic_user_transient(key: str) -> str:
 957        return f"api:chat:user:transient:{key}"
 958
 959    # Join a user-scoped transient (ephemeral) thread
 960    @classmethod
 961    async def join_user_transient(
 962        cls,
 963        socket: "Socket",
 964        key: str,
 965        *,
 966        after_cursor: str | None = None,
 967        before_cursor: str | None = None,
 968        include_metadata: bool | None = None,
 969        limit: int | None = None,
 970    ) -> "ApiChatChannel":
 971        topic = cls.topic_user_transient(key)
 972        channel = socket.channel(topic)
 973        payload: dict[str, object] = {}
 974        if after_cursor is not None:
 975            payload["after_cursor"] = after_cursor
 976        if before_cursor is not None:
 977            payload["before_cursor"] = before_cursor
 978        if include_metadata is not None:
 979            payload["include_metadata"] = include_metadata
 980        if limit is not None:
 981            payload["limit"] = limit
 982        join_response = await channel.join(payload)
 983        return cls(channel, join_response)
 984
 985    # Leave the underlying channel.
 986    async def leave(self):
 987        await self._channel.leave()
 988
 989    # Fork a sub-thread from an existing message
 990    async def api_chat_fork_thread(self, payload: ApiChatForkThreadInput) -> dict[str, Any]:
 991        return await self._channel.push("api:chat:fork_thread", payload)
 992
 993    # Mark a thread as read up to a given message
 994    async def api_chat_mark_thread_read(
 995        self, payload: ApiChatMarkThreadReadInput
 996    ) -> dict[str, Any]:
 997        return await self._channel.push("api:chat:mark_thread_read", payload)
 998
 999    # List all messages in the current thread
1000    async def api_chat_list_messages(self, payload: dict) -> dict[str, Any]:
1001        return await self._channel.push("api:chat:list_messages", payload)
1002
1003    # Load additional messages with cursor-based pagination
1004    async def api_chat_load_more_messages(
1005        self, payload: ApiChatLoadMoreMessagesInput
1006    ) -> dict[str, Any]:
1007        return await self._channel.push("api:chat:load_more_messages", payload)
1008
1009    # Post a new message with optional uploads and reply-to
1010    async def api_chat_post_message(self, payload: ApiChatPostMessageInput) -> dict[str, Any]:
1011        return await self._channel.push("api:chat:post_message", payload)
1012
1013    # Post a simple text message
1014    async def api_chat_post_simple_message(
1015        self, payload: ApiChatPostSimpleMessageInput
1016    ) -> dict[str, Any]:
1017        return await self._channel.push("api:chat:post_simple_message", payload)
1018
1019    # Edit an existing message's content
1020    async def api_chat_edit_message(self, payload: ApiChatEditMessageInput) -> dict[str, Any]:
1021        return await self._channel.push("api:chat:edit_message", payload)
1022
1023    # Delete a message
1024    async def api_chat_delete_message(self, payload: ApiChatDeleteMessageInput) -> dict[str, Any]:
1025        return await self._channel.push("api:chat:delete_message", payload)
1026
1027    # Add an emoji reaction to a message
1028    async def api_chat_add_reaction(self, payload: ApiChatAddReactionInput) -> dict[str, Any]:
1029        return await self._channel.push("api:chat:add_reaction", payload)
1030
1031    # Remove an emoji reaction from a message
1032    async def api_chat_remove_reaction(self, payload: ApiChatRemoveReactionInput) -> dict[str, Any]:
1033        return await self._channel.push("api:chat:remove_reaction", payload)
1034
1035    # Signal that the current user has started or stopped typing in the thread
1036    async def api_chat_typing(self, payload: ApiChatTypingInput) -> dict[str, Any]:
1037        return await self._channel.push("api:chat:typing", payload)
1038
1039    # Replace the local tools advertised by this connection for later messages it posts
1040    async def api_chat_publish_local_tools(
1041        self, payload: ApiChatPublishLocalToolsInput
1042    ) -> dict[str, Any]:
1043        return await self._channel.push("api:chat:publish_local_tools", payload)
1044
1045    # Return one complete result batch for a connection-local tool request
1046    async def api_chat_local_tool_result(
1047        self, payload: ApiChatLocalToolResultInput
1048    ) -> dict[str, Any]:
1049        return await self._channel.push("api:chat:local_tool_result", payload)
1050
1051    # Broadcast when a new message is added to a thread
1052    def on_message_added(
1053        self, callback: Callable[[MessageAddedPayload], None]
1054    ) -> Callable[[], None]:
1055        return self._channel.on("message_added", callback)
1056
1057    # Broadcast when a message is updated or removed
1058    def on_message_updated(
1059        self, callback: Callable[[MessageUpdatedPayload], None]
1060    ) -> Callable[[], None]:
1061        return self._channel.on("message_updated", callback)
1062
1063    # Broadcast thread-level events (agent updates, read state, unread counts)
1064    def on_thread_event(self, callback: Callable[[ThreadEventPayload], None]) -> Callable[[], None]:
1065        return self._channel.on("thread_event", callback)
1066
1067    # Broadcast system-wide events
1068    def on_system_event(self, callback: Callable[[SystemEventPayload], None]) -> Callable[[], None]:
1069        return self._channel.on("system_event", callback)
1070
1071    # Broadcast when a participant (human or agent) starts or stops typing. Ephemeral; never persisted.
1072    def on_typing(self, callback: Callable[[TypingPayload], None]) -> Callable[[], None]:
1073        return self._channel.on("typing", callback)
1074
1075    # Invoke local functions on the exact connection that published them
1076    def on_local_tool_call(
1077        self, callback: Callable[[LocalToolCallPayload], None]
1078    ) -> Callable[[], None]:
1079        return self._channel.on("local_tool_call", callback)
1080
1081    # Stop local work because the server no longer accepts its result
1082    def on_local_tool_cancelled(
1083        self, callback: Callable[[LocalToolCancelledPayload], None]
1084    ) -> Callable[[], None]:
1085        return self._channel.on("local_tool_cancelled", callback)
class ApiChatForkThreadInput(typing.TypedDict):
14class ApiChatForkThreadInput(TypedDict, total=False):
15    "Fork a sub-thread from an existing message"
16
17    message_id: Required[str]
18    title: str

Fork a sub-thread from an existing message

message_id: Required[str]
title: str
class ApiChatMarkThreadReadInput(typing.TypedDict):
21class ApiChatMarkThreadReadInput(TypedDict):
22    "Mark a thread as read up to a given message"
23
24    message_id: str

Mark a thread as read up to a given message

message_id: str
class ApiChatLoadMoreMessagesInput(typing.TypedDict):
27class ApiChatLoadMoreMessagesInput(TypedDict, total=False):
28    "Load additional messages with cursor-based pagination"
29
30    after_cursor: str
31    before_cursor: str
32    include_metadata: bool
33    limit: int

Load additional messages with cursor-based pagination

after_cursor: str
before_cursor: str
include_metadata: bool
limit: int
class ApiChatPostMessageInputContextItem(typing.TypedDict):
36class ApiChatPostMessageInputContextItem(TypedDict, total=False):
37    attributes: dict[str, Any] | None
38    "Scalar key-value fields describing the context, such as route, repository, or pull-request number."
39    content: str | None
40    "Optional context body. The model receives it as escaped XML data, not a system instruction."
41    title: str | None
42    "Optional human-readable label for this context block."
43    type: Required[str]
44    "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores."
attributes: dict[str, typing.Any] | None

Scalar key-value fields describing the context, such as route, repository, or pull-request number.

content: str | None

Optional context body. The model receives it as escaped XML data, not a system instruction.

title: str | None

Optional human-readable label for this context block.

type: Required[str]

Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.

class ApiChatPostMessageInput(typing.TypedDict):
47class ApiChatPostMessageInput(TypedDict, total=False):
48    "Post a new message with optional uploads and reply-to"
49
50    content: Required[str]
51    context: list[ApiChatPostMessageInputContextItem]
52    idempotency_key: str
53    mentions: list[str]
54    metadata: dict[str, Any]
55    reply_to: str
56    uploads: list[dict[str, Any]]

Post a new message with optional uploads and reply-to

content: Required[str]
idempotency_key: str
mentions: list[str]
metadata: dict[str, typing.Any]
reply_to: str
uploads: list[dict[str, typing.Any]]
class ApiChatPostSimpleMessageInputContextItem(typing.TypedDict):
59class ApiChatPostSimpleMessageInputContextItem(TypedDict, total=False):
60    attributes: dict[str, Any] | None
61    "Scalar key-value fields describing the context, such as route, repository, or pull-request number."
62    content: str | None
63    "Optional context body. The model receives it as escaped XML data, not a system instruction."
64    title: str | None
65    "Optional human-readable label for this context block."
66    type: Required[str]
67    "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores."
attributes: dict[str, typing.Any] | None

Scalar key-value fields describing the context, such as route, repository, or pull-request number.

content: str | None

Optional context body. The model receives it as escaped XML data, not a system instruction.

title: str | None

Optional human-readable label for this context block.

type: Required[str]

Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.

class ApiChatPostSimpleMessageInput(typing.TypedDict):
70class ApiChatPostSimpleMessageInput(TypedDict, total=False):
71    "Post a simple text message"
72
73    content: str
74    context: list[ApiChatPostSimpleMessageInputContextItem]
75    idempotency_key: str
76    mentions: list[str]
77    metadata: dict[str, Any]
78    reply_to: str

Post a simple text message

content: str
idempotency_key: str
mentions: list[str]
metadata: dict[str, typing.Any]
reply_to: str
class ApiChatEditMessageInput(typing.TypedDict):
81class ApiChatEditMessageInput(TypedDict):
82    "Edit an existing message's content"
83
84    content: str
85    message_id: str

Edit an existing message's content

content: str
message_id: str
class ApiChatDeleteMessageInput(typing.TypedDict):
88class ApiChatDeleteMessageInput(TypedDict):
89    "Delete a message"
90
91    message_id: str

Delete a message

message_id: str
class ApiChatAddReactionInput(typing.TypedDict):
94class ApiChatAddReactionInput(TypedDict):
95    "Add an emoji reaction to a message"
96
97    emoji: str
98    message_id: str

Add an emoji reaction to a message

emoji: str
message_id: str
class ApiChatRemoveReactionInput(typing.TypedDict):
101class ApiChatRemoveReactionInput(TypedDict):
102    "Remove an emoji reaction from a message"
103
104    emoji: str
105    message_id: str

Remove an emoji reaction from a message

emoji: str
message_id: str
class ApiChatTypingInput(typing.TypedDict):
108class ApiChatTypingInput(TypedDict):
109    "Signal that the current user has started or stopped typing in the thread"
110
111    is_typing: bool

Signal that the current user has started or stopped typing in the thread

is_typing: bool
class ApiChatPublishLocalToolsInputLocalToolsItemFunction(typing.TypedDict):
114class ApiChatPublishLocalToolsInputLocalToolsItemFunction(TypedDict):
115    description: str
116    name: str
117    parameters: dict[str, Any]
description: str
name: str
parameters: dict[str, typing.Any]
class ApiChatPublishLocalToolsInputLocalToolsItem(typing.TypedDict):
120class ApiChatPublishLocalToolsInputLocalToolsItem(TypedDict):
121    function: ApiChatPublishLocalToolsInputLocalToolsItemFunction
122    "A function implemented by the client connected to a personal thread."
123    type: Literal["function"]

A function implemented by the client connected to a personal thread.

type: Literal['function']
class ApiChatPublishLocalToolsInput(typing.TypedDict):
126class ApiChatPublishLocalToolsInput(TypedDict):
127    "Replace the local tools advertised by this connection for later messages it posts"
128
129    local_tool_provider_id: str
130    local_tools: list[ApiChatPublishLocalToolsInputLocalToolsItem]

Replace the local tools advertised by this connection for later messages it posts

local_tool_provider_id: str
class ApiChatLocalToolResultInput(typing.TypedDict):
133class ApiChatLocalToolResultInput(TypedDict):
134    "Return one complete result batch for a connection-local tool request"
135
136    agent_id: str
137    generation: int
138    provider_id: str
139    request_id: str
140    results: list[dict[str, Any] | dict[str, Any] | dict[str, Any]]

Return one complete result batch for a connection-local tool request

agent_id: str
generation: int
provider_id: str
request_id: str
results: list[dict[str, typing.Any]]
class MessageAddedPayloadMessageAclAddItem(typing.TypedDict):
143class MessageAddedPayloadMessageAclAddItem(TypedDict, total=False):
144    actions: Required[list[str]]
145    'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.'
146    principal: str | None
147    '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"`.'
148    principal_type: Required[str]
149    '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 MessageAddedPayloadMessageAclGrantsItem(typing.TypedDict):
152class MessageAddedPayloadMessageAclGrantsItem(TypedDict, total=False):
153    actions: Required[list[str]]
154    'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.'
155    principal: str | None
156    '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"`.'
157    principal_type: Required[str]
158    '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 MessageAddedPayloadMessageAclRemoveItem(typing.TypedDict):
161class MessageAddedPayloadMessageAclRemoveItem(TypedDict, total=False):
162    principal: str | None
163    '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"`.'
164    principal_type: Required[str]
165    '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 MessageAddedPayloadMessageAcl(typing.TypedDict):
168class MessageAddedPayloadMessageAcl(TypedDict, total=False):
169    add: list[MessageAddedPayloadMessageAclAddItem] | None
170    "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`."
171    grants: list[MessageAddedPayloadMessageAclGrantsItem] | None
172    "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`."
173    remove: list[MessageAddedPayloadMessageAclRemoveItem] | None
174    "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`."

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

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.

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

class MessageAddedPayloadMessageActorsItemProfilePicture(typing.TypedDict):
177class MessageAddedPayloadMessageActorsItemProfilePicture(TypedDict, total=False):
178    file: str | None
179    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
180    height: int | None
181    "Height of the image in pixels. `null` if not known."
182    media: str | None
183    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
184    mime_type: str | None
185    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
186    refresh_url: str | None
187    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
188    url: str | None
189    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
190    width: int | None
191    "Width of the image in pixels. `null` if not known."
file: str | None

ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.

height: int | None

Height of the image in pixels. null if not known.

media: str | None

ID of the associated media record (med_...). null when the image is not linked to a media entity.

mime_type: str | None

MIME type of the image, e.g. "image/png" or "image/jpeg". null if not known.

refresh_url: str | None

Endpoint URL you can call to obtain a fresh signed url when the current one has expired. null if the URL does not require refreshing.

url: str | None

Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.

width: int | None

Width of the image in pixels. null if not known.

class MessageAddedPayloadMessageActorsItem(typing.TypedDict):
194class MessageAddedPayloadMessageActorsItem(TypedDict, total=False):
195    alias: str | None
196    "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured."
197    id: str | None
198    'Composite actor identifier. Format is `"user-<usr_...>"` for human users or `"agent-<agi_...>"` for agents.'
199    name: str | None
200    "Display name of the actor shown in the UI. `null` if no name is set."
201    profile_picture: MessageAddedPayloadMessageActorsItemProfilePicture | None
202    "Profile picture for the actor. `null` if the actor has no profile picture."
alias: str | None

Short handle or alias for the actor, used as an alternate display identifier. null if not configured.

id: str | None

Composite actor identifier. Format is "user-<usr_...>" for human users or "agent-<agi_...>" for agents.

name: str | None

Display name of the actor shown in the UI. null if no name is set.

Profile picture for the actor. null if the actor has no profile picture.

class MessageAddedPayloadMessageAttachmentsItemImageSource(typing.TypedDict):
205class MessageAddedPayloadMessageAttachmentsItemImageSource(TypedDict, total=False):
206    file: str | None
207    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
208    height: int | None
209    "Height of the image in pixels. `null` if not known."
210    media: str | None
211    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
212    mime_type: str | None
213    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
214    refresh_url: str | None
215    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
216    url: str | None
217    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
218    width: int | None
219    "Width of the image in pixels. `null` if not known."
file: str | None

ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.

height: int | None

Height of the image in pixels. null if not known.

media: str | None

ID of the associated media record (med_...). null when the image is not linked to a media entity.

mime_type: str | None

MIME type of the image, e.g. "image/png" or "image/jpeg". null if not known.

refresh_url: str | None

Endpoint URL you can call to obtain a fresh signed url when the current one has expired. null if the URL does not require refreshing.

url: str | None

Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.

width: int | None

Width of the image in pixels. null if not known.

class MessageAddedPayloadMessageAttachmentsItemVariantsItemImageSource(typing.TypedDict):
222class MessageAddedPayloadMessageAttachmentsItemVariantsItemImageSource(TypedDict, total=False):
223    file: str | None
224    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
225    height: int | None
226    "Height of the image in pixels. `null` if not known."
227    media: str | None
228    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
229    mime_type: str | None
230    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
231    refresh_url: str | None
232    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
233    url: str | None
234    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
235    width: int | None
236    "Width of the image in pixels. `null` if not known."
file: str | None

ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.

height: int | None

Height of the image in pixels. null if not known.

media: str | None

ID of the associated media record (med_...). null when the image is not linked to a media entity.

mime_type: str | None

MIME type of the image, e.g. "image/png" or "image/jpeg". null if not known.

refresh_url: str | None

Endpoint URL you can call to obtain a fresh signed url when the current one has expired. null if the URL does not require refreshing.

url: str | None

Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.

width: int | None

Width of the image in pixels. null if not known.

class MessageAddedPayloadMessageAttachmentsItemVariantsItem(typing.TypedDict):
239class MessageAddedPayloadMessageAttachmentsItemVariantsItem(TypedDict, total=False):
240    byte_size: int | None
241    "Size of the stored bytes."
242    content_type: str | None
243    'MIME type of this variant\'s file (e.g., `"image/jpeg"`, `"video/mp4"`). `null` if the file is not loaded.'
244    created_at: datetime | None
245    "When this variant was created (ISO 8601)."
246    duration_ms: int | None
247    "Client-declared video duration in milliseconds; not server-probed."
248    file: str | None
249    "ID of the underlying storage file that backs this variant (`fil_...`)."
250    filename: str | None
251    "Original filename of the uploaded file for this variant. `null` if the file is not loaded."
252    height: int | None
253    "Height of this variant in pixels. `null` if not recorded."
254    id: Required[str]
255    "Media variant ID (`mvr_...`)."
256    image_source: MessageAddedPayloadMessageAttachmentsItemVariantsItemImageSource | None
257    "Resolved image delivery metadata for this variant, including dimensions and CDN URL. `null` for non-image content types."
258    sha256: str | None
259    "Lowercase SHA-256 of the stored bytes."
260    updated_at: datetime | None
261    "When this variant was last updated (ISO 8601)."
262    url: str | None
263    "Signed download URL for this variant, resolved at request time. `null` if the file is unavailable."
264    url_expires_at: datetime | None
265    "Expiry of this variant's playable URL."
266    variant_key: str | None
267    'Identifier for this variant\'s processing tier. Common values include `"original"` (the unmodified upload) and `"thumbnail"` (a resized preview).'
268    width: int | None
269    "Width of this variant in pixels. `null` if not recorded."
byte_size: int | None

Size of the stored bytes.

content_type: str | None

MIME type of this variant's file (e.g., "image/jpeg", "video/mp4"). null if the file is not loaded.

created_at: datetime.datetime | None

When this variant was created (ISO 8601).

duration_ms: int | None

Client-declared video duration in milliseconds; not server-probed.

file: str | None

ID of the underlying storage file that backs this variant (fil_...).

filename: str | None

Original filename of the uploaded file for this variant. null if the file is not loaded.

height: int | None

Height of this variant in pixels. null if not recorded.

id: Required[str]

Media variant ID (mvr_...).

Resolved image delivery metadata for this variant, including dimensions and CDN URL. null for non-image content types.

sha256: str | None

Lowercase SHA-256 of the stored bytes.

updated_at: datetime.datetime | None

When this variant was last updated (ISO 8601).

url: str | None

Signed download URL for this variant, resolved at request time. null if the file is unavailable.

url_expires_at: datetime.datetime | None

Expiry of this variant's playable URL.

variant_key: str | None

Identifier for this variant's processing tier. Common values include "original" (the unmodified upload) and "thumbnail" (a resized preview).

width: int | None

Width of this variant in pixels. null if not recorded.

class MessageAddedPayloadMessageAttachmentsItem(typing.TypedDict):
272class MessageAddedPayloadMessageAttachmentsItem(TypedDict, total=False):
273    content_type: str | None
274    'MIME type of the attached file, e.g. `"image/png"` or `"application/pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.'
275    description: str | None
276    "Short description. The page meta-description for `scraped_link`, the artifact description for `artifact`, and the task description for `task` types. `null` on other types."
277    filename: str | None
278    'Original filename of the attached file, e.g. `"report.pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.'
279    height: int | None
280    "Height in pixels of the media item. Present on `media` type only. `null` otherwise."
281    id: Required[str]
282    "Unique identifier for this attachment within the message."
283    image_height: int | None
284    "Height in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise."
285    image_source: MessageAddedPayloadMessageAttachmentsItemImageSource | None
286    "Image source metadata for inline rendering. Present on `file`, `scraped_link`, `artifact`, and `media` types when the content is an image. `null` otherwise."
287    image_url: str | None
288    "URL of the preview image extracted from the scraped page. Present on `scraped_link` type only. `null` otherwise."
289    image_width: int | None
290    "Width in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise."
291    key: str | None
292    'For `structured_data` type, what the data is about as set by the sender (for example `"pr:14963"`), copied from the record so it can be read without `object`. Not unique. `null` when unset and on other types.'
293    media_type: str | None
294    'The media category, e.g. `"video"` or `"audio"`. Present on `media` type only; omitted otherwise.'
295    name: str | None
296    "Display name of the media item. Present on `media` type only. `null` otherwise."
297    object: dict[str, Any] | None
298    "The full embedded object payload. For `task` type, contains the task record. For `action` type, contains the action definition. For `chart` type, contains the chart with its inline `spec`. For `structured_data` type, contains the inline `data` and the `schema_url` or `schema_config` naming its JSON Schema. Omitted on other types."
299    title: str | None
300    "Display title. The page title for `scraped_link`, the artifact name for `artifact`, and the task title for `task` types. `null` on other types."
301    type: Required[str]
302    'The attachment type. One of `"file"`, `"scraped_link"`, `"artifact"`, `"task"`, `"media"`, `"action"`, `"chart"`, or `"structured_data"`. Determines which additional fields are present.'
303    url: str | None
304    "URL to access the resource. A signed download URL for `file` and `artifact` types; the original URL for `scraped_link`; a media playback URL for `media`. `null` on `task` and `action` types."
305    variants: list[MessageAddedPayloadMessageAttachmentsItemVariantsItem] | None
306    "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise."
307    version: int | None
308    "Version number of the attached artifact at the time of attachment. Present on `artifact` type only. `null` otherwise."
309    width: int | None
310    "Width in pixels of the media item. Present on `media` type only. `null` otherwise."
content_type: str | None

MIME type of the attached file, e.g. "image/png" or "application/pdf". Present on file, artifact, and media types. null otherwise.

description: str | None

Short description. The page meta-description for scraped_link, the artifact description for artifact, and the task description for task types. null on other types.

filename: str | None

Original filename of the attached file, e.g. "report.pdf". Present on file, artifact, and media types. null otherwise.

height: int | None

Height in pixels of the media item. Present on media type only. null otherwise.

id: Required[str]

Unique identifier for this attachment within the message.

image_height: int | None

Height in pixels of the scraped preview image. Present on scraped_link type only. null otherwise.

Image source metadata for inline rendering. Present on file, scraped_link, artifact, and media types when the content is an image. null otherwise.

image_url: str | None

URL of the preview image extracted from the scraped page. Present on scraped_link type only. null otherwise.

image_width: int | None

Width in pixels of the scraped preview image. Present on scraped_link type only. null otherwise.

key: str | None

For structured_data type, what the data is about as set by the sender (for example "pr:14963"), copied from the record so it can be read without object. Not unique. null when unset and on other types.

media_type: str | None

The media category, e.g. "video" or "audio". Present on media type only; omitted otherwise.

name: str | None

Display name of the media item. Present on media type only. null otherwise.

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

The full embedded object payload. For task type, contains the task record. For action type, contains the action definition. For chart type, contains the chart with its inline spec. For structured_data type, contains the inline data and the schema_url or schema_config naming its JSON Schema. Omitted on other types.

title: str | None

Display title. The page title for scraped_link, the artifact name for artifact, and the task title for task types. null on other types.

type: Required[str]

The attachment type. One of "file", "scraped_link", "artifact", "task", "media", "action", "chart", or "structured_data". Determines which additional fields are present.

url: str | None

URL to access the resource. A signed download URL for file and artifact types; the original URL for scraped_link; a media playback URL for media. null on task and action types.

Array of available encoding variants for the media item (e.g. different resolutions). Present on media type only; omitted otherwise.

version: int | None

Version number of the attached artifact at the time of attachment. Present on artifact type only. null otherwise.

width: int | None

Width in pixels of the media item. Present on media type only. null otherwise.

class MessageAddedPayloadMessageContextItem(typing.TypedDict):
313class MessageAddedPayloadMessageContextItem(TypedDict, total=False):
314    attributes: dict[str, Any] | None
315    "Scalar key-value fields describing the context, such as route, repository, or pull-request number."
316    content: str | None
317    "Optional context body. The model receives it as escaped XML data, not a system instruction."
318    title: str | None
319    "Optional human-readable label for this context block."
320    type: Required[str]
321    "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores."
attributes: dict[str, typing.Any] | None

Scalar key-value fields describing the context, such as route, repository, or pull-request number.

content: str | None

Optional context body. The model receives it as escaped XML data, not a system instruction.

title: str | None

Optional human-readable label for this context block.

type: Required[str]

Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.

class MessageAddedPayloadMessageReactionsItem(typing.TypedDict):
324class MessageAddedPayloadMessageReactionsItem(TypedDict, total=False):
325    payload: dict[str, Any] | None
326    'Type-specific reaction data. For `"emoji_reaction"` reactions, contains an `emoji` key with the Unicode emoji string (e.g., `" "`).'
327    type: Required[str]
328    'Reaction type identifier. Currently always `"emoji_reaction"` for emoji-based reactions.'
329    user: str | None
330    "Public ID of the user who added the reaction (`usr_...`)."
payload: dict[str, typing.Any] | None

Type-specific reaction data. For "emoji_reaction" reactions, contains an emoji key with the Unicode emoji string (e.g., " ").

type: Required[str]

Reaction type identifier. Currently always "emoji_reaction" for emoji-based reactions.

user: str | None

Public ID of the user who added the reaction (usr_...).

class MessageAddedPayloadMessage(typing.TypedDict):
333class MessageAddedPayloadMessage(TypedDict, total=False):
334    acl: MessageAddedPayloadMessageAcl | None
335    "Access control list for private messages (grants with `read` action). Only returned to resource owners (and privileged/org-admin viewers) via server-side `field_redactions: [acl: :owner]`; `null` for everyone else."
336    actors: list[MessageAddedPayloadMessageActorsItem] | None
337    "Resolved actor descriptors for the message sender, combining identity and display metadata. Always contains exactly one entry."
338    agent: str | None
339    "ID of the agent user that sent this message (`agi_...`). `null` for messages sent by human users."
340    agent_mode: Literal["cli", "embedded"] | None
341    "Local agent execution mode for this message. One of `cli`, `embedded`, or `null` when the message was not created by a local agent execution path."
342    attachments: list[MessageAddedPayloadMessageAttachmentsItem] | None
343    "Files, links, tasks, media, artifacts, and actions attached to this message. Empty array if there are no attachments."
344    branched_thread: str | None
345    "ID of the thread that was branched from this message (`thr_...`). `null` if this message has not spawned a branch thread."
346    content: str | None
347    "Text content of the message. `null` for messages that contain only attachments."
348    context: list[MessageAddedPayloadMessageContextItem] | None
349    "Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions."
350    created_at: str | None
351    "When the message was posted (ISO 8601)."
352    has_replies: bool | None
353    "Whether this message has at least one reply. Only present when explicitly requested or computed by the server."
354    id: Required[str]
355    "Message ID (`msg_...`)."
356    idempotency_key: str | None
357    "Client-supplied idempotency key used to deduplicate message sends. `null` if the sender did not provide one."
358    is_deleted: bool | None
359    "Whether this message is a deletion tombstone. `true` only on the `message_updated` broadcast emitted when a message is deleted: the original content is replaced with a placeholder and the message no longer exists on the server. Always `false` for live messages."
360    legacy_agent: str | None
361    "Identifier of the legacy chat agent that sent this message, if applicable. `null` for messages sent by users or modern agent users."
362    metadata: dict[str, Any] | None
363    "Arbitrary key-value metadata attached to the message. Always present; defaults to an empty object when no metadata has been set."
364    org: str | None
365    "ID of the organization that owns this message (`org_...`)."
366    reactions: list[MessageAddedPayloadMessageReactionsItem] | None
367    "Emoji and other reactions added to this message by users. Empty array if no reactions have been added or the association is not preloaded."
368    rendering_mode: str | None
369    'Display hint for how the message should be rendered. One of `"reply"`, `"direct"`, or `"inline"`. `null` for user-authored messages, which are always rendered as standard replies.'
370    replies: list[dict[str, Any]] | None
371    "Inline array of reply messages, each serialized as a full message object. Only present when the server has preloaded replies for this message."
372    replies_after_cursor: str | None
373    "Opaque pagination cursor to fetch replies posted after the current page. Only present when inline replies are included in the response."
374    replies_before_cursor: str | None
375    "Opaque pagination cursor to fetch replies posted before the current page. Only present when inline replies are included in the response."
376    reply_count: int | None
377    "Total number of direct replies to this message. Only present when explicitly requested or computed by the server."
378    reply_to: dict[str, Any] | None
379    "The parent message this message is a reply to, expanded as a full message object when loaded. `null` if this is a top-level message or the association is not preloaded."
380    root_message_id: str | None
381    "ID of the root message in this reply chain (`msg_...`). `null` for a top-level message. The value is persisted when the reply is created, so callers can correlate a multi-turn session without walking parent messages."
382    sandbox: str | None
383    "ID of the developer sandbox this message belongs to (`dsb_...`). `null` for non-sandbox messages."
384    team: str | None
385    "ID of the team this message is scoped to (`tem_...`). `null` if the message is not team-scoped."
386    thread: str | None
387    "ID of the thread this message belongs to (`thr_...`)."
388    type: str | None
389    "Optional client-defined classification for the message (for example `note` or `status`). Free-form string up to 64 characters. The value `system` is reserved for platform-authored messages and cannot be set by clients. `null` when unset."
390    user: str | dict[str, Any] | None
391    "The human user who sent this message. Returns a public ID string (`usr_...`) when the association is not preloaded, or an expanded user object when it is. `null` for messages sent by agents."
392    visibility: Literal["default", "private"] | None
393    "Message-level visibility. `default` is visible to anyone who can see the parent thread. `private` is restricted to the sender and explicit ACL `read` grantees."

Access control list for private messages (grants with read action). Only returned to resource owners (and privileged/org-admin viewers) via server-side field_redactions: [acl: :owner]; null for everyone else.

Resolved actor descriptors for the message sender, combining identity and display metadata. Always contains exactly one entry.

agent: str | None

ID of the agent user that sent this message (agi_...). null for messages sent by human users.

agent_mode: Optional[Literal['cli', 'embedded']]

Local agent execution mode for this message. One of cli, embedded, or null when the message was not created by a local agent execution path.

attachments: list[MessageAddedPayloadMessageAttachmentsItem] | None

Files, links, tasks, media, artifacts, and actions attached to this message. Empty array if there are no attachments.

branched_thread: str | None

ID of the thread that was branched from this message (thr_...). null if this message has not spawned a branch thread.

content: str | None

Text content of the message. null for messages that contain only attachments.

Immutable structured context captured when the message was posted. Each entry has type, optional title and content, and scalar attributes. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions.

created_at: str | None

When the message was posted (ISO 8601).

has_replies: bool | None

Whether this message has at least one reply. Only present when explicitly requested or computed by the server.

id: Required[str]

Message ID (msg_...).

idempotency_key: str | None

Client-supplied idempotency key used to deduplicate message sends. null if the sender did not provide one.

is_deleted: bool | None

Whether this message is a deletion tombstone. true only on the message_updated broadcast emitted when a message is deleted: the original content is replaced with a placeholder and the message no longer exists on the server. Always false for live messages.

legacy_agent: str | None

Identifier of the legacy chat agent that sent this message, if applicable. null for messages sent by users or modern agent users.

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

Arbitrary key-value metadata attached to the message. Always present; defaults to an empty object when no metadata has been set.

org: str | None

ID of the organization that owns this message (org_...).

reactions: list[MessageAddedPayloadMessageReactionsItem] | None

Emoji and other reactions added to this message by users. Empty array if no reactions have been added or the association is not preloaded.

rendering_mode: str | None

Display hint for how the message should be rendered. One of "reply", "direct", or "inline". null for user-authored messages, which are always rendered as standard replies.

replies: list[dict[str, typing.Any]] | None

Inline array of reply messages, each serialized as a full message object. Only present when the server has preloaded replies for this message.

replies_after_cursor: str | None

Opaque pagination cursor to fetch replies posted after the current page. Only present when inline replies are included in the response.

replies_before_cursor: str | None

Opaque pagination cursor to fetch replies posted before the current page. Only present when inline replies are included in the response.

reply_count: int | None

Total number of direct replies to this message. Only present when explicitly requested or computed by the server.

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

The parent message this message is a reply to, expanded as a full message object when loaded. null if this is a top-level message or the association is not preloaded.

root_message_id: str | None

ID of the root message in this reply chain (msg_...). null for a top-level message. The value is persisted when the reply is created, so callers can correlate a multi-turn session without walking parent messages.

sandbox: str | None

ID of the developer sandbox this message belongs to (dsb_...). null for non-sandbox messages.

team: str | None

ID of the team this message is scoped to (tem_...). null if the message is not team-scoped.

thread: str | None

ID of the thread this message belongs to (thr_...).

type: str | None

Optional client-defined classification for the message (for example note or status). Free-form string up to 64 characters. The value system is reserved for platform-authored messages and cannot be set by clients. null when unset.

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

The human user who sent this message. Returns a public ID string (usr_...) when the association is not preloaded, or an expanded user object when it is. null for messages sent by agents.

visibility: Optional[Literal['default', 'private']]

Message-level visibility. default is visible to anyone who can see the parent thread. private is restricted to the sender and explicit ACL read grantees.

class MessageAddedPayload(typing.TypedDict):
396class MessageAddedPayload(TypedDict, total=False):
397    "Broadcast when a new message is added to a thread"
398
399    after_cursor: str | None
400    before_cursor: str | None
401    message: MessageAddedPayloadMessage | None
402    "A chat message posted in a thread, including its content, author, attachments, reactions, and optional reply metadata."
403    thread_id: str | None

Broadcast when a new message is added to a thread

after_cursor: str | None
before_cursor: str | None
message: MessageAddedPayloadMessage | None

A chat message posted in a thread, including its content, author, attachments, reactions, and optional reply metadata.

thread_id: str | None
class MessageUpdatedPayloadMessageAclAddItem(typing.TypedDict):
406class MessageUpdatedPayloadMessageAclAddItem(TypedDict, total=False):
407    actions: Required[list[str]]
408    'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.'
409    principal: str | None
410    '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"`.'
411    principal_type: Required[str]
412    '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 MessageUpdatedPayloadMessageAclGrantsItem(typing.TypedDict):
415class MessageUpdatedPayloadMessageAclGrantsItem(TypedDict, total=False):
416    actions: Required[list[str]]
417    'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.'
418    principal: str | None
419    '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"`.'
420    principal_type: Required[str]
421    '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 MessageUpdatedPayloadMessageAclRemoveItem(typing.TypedDict):
424class MessageUpdatedPayloadMessageAclRemoveItem(TypedDict, total=False):
425    principal: str | None
426    '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"`.'
427    principal_type: Required[str]
428    '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 MessageUpdatedPayloadMessageAcl(typing.TypedDict):
431class MessageUpdatedPayloadMessageAcl(TypedDict, total=False):
432    add: list[MessageUpdatedPayloadMessageAclAddItem] | None
433    "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`."
434    grants: list[MessageUpdatedPayloadMessageAclGrantsItem] | None
435    "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`."
436    remove: list[MessageUpdatedPayloadMessageAclRemoveItem] | None
437    "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`."

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

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.

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

class MessageUpdatedPayloadMessageActorsItemProfilePicture(typing.TypedDict):
440class MessageUpdatedPayloadMessageActorsItemProfilePicture(TypedDict, total=False):
441    file: str | None
442    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
443    height: int | None
444    "Height of the image in pixels. `null` if not known."
445    media: str | None
446    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
447    mime_type: str | None
448    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
449    refresh_url: str | None
450    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
451    url: str | None
452    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
453    width: int | None
454    "Width of the image in pixels. `null` if not known."
file: str | None

ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.

height: int | None

Height of the image in pixels. null if not known.

media: str | None

ID of the associated media record (med_...). null when the image is not linked to a media entity.

mime_type: str | None

MIME type of the image, e.g. "image/png" or "image/jpeg". null if not known.

refresh_url: str | None

Endpoint URL you can call to obtain a fresh signed url when the current one has expired. null if the URL does not require refreshing.

url: str | None

Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.

width: int | None

Width of the image in pixels. null if not known.

class MessageUpdatedPayloadMessageActorsItem(typing.TypedDict):
457class MessageUpdatedPayloadMessageActorsItem(TypedDict, total=False):
458    alias: str | None
459    "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured."
460    id: str | None
461    'Composite actor identifier. Format is `"user-<usr_...>"` for human users or `"agent-<agi_...>"` for agents.'
462    name: str | None
463    "Display name of the actor shown in the UI. `null` if no name is set."
464    profile_picture: MessageUpdatedPayloadMessageActorsItemProfilePicture | None
465    "Profile picture for the actor. `null` if the actor has no profile picture."
alias: str | None

Short handle or alias for the actor, used as an alternate display identifier. null if not configured.

id: str | None

Composite actor identifier. Format is "user-<usr_...>" for human users or "agent-<agi_...>" for agents.

name: str | None

Display name of the actor shown in the UI. null if no name is set.

Profile picture for the actor. null if the actor has no profile picture.

class MessageUpdatedPayloadMessageAttachmentsItemImageSource(typing.TypedDict):
468class MessageUpdatedPayloadMessageAttachmentsItemImageSource(TypedDict, total=False):
469    file: str | None
470    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
471    height: int | None
472    "Height of the image in pixels. `null` if not known."
473    media: str | None
474    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
475    mime_type: str | None
476    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
477    refresh_url: str | None
478    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
479    url: str | None
480    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
481    width: int | None
482    "Width of the image in pixels. `null` if not known."
file: str | None

ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.

height: int | None

Height of the image in pixels. null if not known.

media: str | None

ID of the associated media record (med_...). null when the image is not linked to a media entity.

mime_type: str | None

MIME type of the image, e.g. "image/png" or "image/jpeg". null if not known.

refresh_url: str | None

Endpoint URL you can call to obtain a fresh signed url when the current one has expired. null if the URL does not require refreshing.

url: str | None

Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.

width: int | None

Width of the image in pixels. null if not known.

class MessageUpdatedPayloadMessageAttachmentsItemVariantsItemImageSource(typing.TypedDict):
485class MessageUpdatedPayloadMessageAttachmentsItemVariantsItemImageSource(TypedDict, total=False):
486    file: str | None
487    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
488    height: int | None
489    "Height of the image in pixels. `null` if not known."
490    media: str | None
491    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
492    mime_type: str | None
493    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
494    refresh_url: str | None
495    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
496    url: str | None
497    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
498    width: int | None
499    "Width of the image in pixels. `null` if not known."
file: str | None

ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.

height: int | None

Height of the image in pixels. null if not known.

media: str | None

ID of the associated media record (med_...). null when the image is not linked to a media entity.

mime_type: str | None

MIME type of the image, e.g. "image/png" or "image/jpeg". null if not known.

refresh_url: str | None

Endpoint URL you can call to obtain a fresh signed url when the current one has expired. null if the URL does not require refreshing.

url: str | None

Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.

width: int | None

Width of the image in pixels. null if not known.

class MessageUpdatedPayloadMessageAttachmentsItemVariantsItem(typing.TypedDict):
502class MessageUpdatedPayloadMessageAttachmentsItemVariantsItem(TypedDict, total=False):
503    byte_size: int | None
504    "Size of the stored bytes."
505    content_type: str | None
506    'MIME type of this variant\'s file (e.g., `"image/jpeg"`, `"video/mp4"`). `null` if the file is not loaded.'
507    created_at: datetime | None
508    "When this variant was created (ISO 8601)."
509    duration_ms: int | None
510    "Client-declared video duration in milliseconds; not server-probed."
511    file: str | None
512    "ID of the underlying storage file that backs this variant (`fil_...`)."
513    filename: str | None
514    "Original filename of the uploaded file for this variant. `null` if the file is not loaded."
515    height: int | None
516    "Height of this variant in pixels. `null` if not recorded."
517    id: Required[str]
518    "Media variant ID (`mvr_...`)."
519    image_source: MessageUpdatedPayloadMessageAttachmentsItemVariantsItemImageSource | None
520    "Resolved image delivery metadata for this variant, including dimensions and CDN URL. `null` for non-image content types."
521    sha256: str | None
522    "Lowercase SHA-256 of the stored bytes."
523    updated_at: datetime | None
524    "When this variant was last updated (ISO 8601)."
525    url: str | None
526    "Signed download URL for this variant, resolved at request time. `null` if the file is unavailable."
527    url_expires_at: datetime | None
528    "Expiry of this variant's playable URL."
529    variant_key: str | None
530    'Identifier for this variant\'s processing tier. Common values include `"original"` (the unmodified upload) and `"thumbnail"` (a resized preview).'
531    width: int | None
532    "Width of this variant in pixels. `null` if not recorded."
byte_size: int | None

Size of the stored bytes.

content_type: str | None

MIME type of this variant's file (e.g., "image/jpeg", "video/mp4"). null if the file is not loaded.

created_at: datetime.datetime | None

When this variant was created (ISO 8601).

duration_ms: int | None

Client-declared video duration in milliseconds; not server-probed.

file: str | None

ID of the underlying storage file that backs this variant (fil_...).

filename: str | None

Original filename of the uploaded file for this variant. null if the file is not loaded.

height: int | None

Height of this variant in pixels. null if not recorded.

id: Required[str]

Media variant ID (mvr_...).

Resolved image delivery metadata for this variant, including dimensions and CDN URL. null for non-image content types.

sha256: str | None

Lowercase SHA-256 of the stored bytes.

updated_at: datetime.datetime | None

When this variant was last updated (ISO 8601).

url: str | None

Signed download URL for this variant, resolved at request time. null if the file is unavailable.

url_expires_at: datetime.datetime | None

Expiry of this variant's playable URL.

variant_key: str | None

Identifier for this variant's processing tier. Common values include "original" (the unmodified upload) and "thumbnail" (a resized preview).

width: int | None

Width of this variant in pixels. null if not recorded.

class MessageUpdatedPayloadMessageAttachmentsItem(typing.TypedDict):
535class MessageUpdatedPayloadMessageAttachmentsItem(TypedDict, total=False):
536    content_type: str | None
537    'MIME type of the attached file, e.g. `"image/png"` or `"application/pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.'
538    description: str | None
539    "Short description. The page meta-description for `scraped_link`, the artifact description for `artifact`, and the task description for `task` types. `null` on other types."
540    filename: str | None
541    'Original filename of the attached file, e.g. `"report.pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.'
542    height: int | None
543    "Height in pixels of the media item. Present on `media` type only. `null` otherwise."
544    id: Required[str]
545    "Unique identifier for this attachment within the message."
546    image_height: int | None
547    "Height in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise."
548    image_source: MessageUpdatedPayloadMessageAttachmentsItemImageSource | None
549    "Image source metadata for inline rendering. Present on `file`, `scraped_link`, `artifact`, and `media` types when the content is an image. `null` otherwise."
550    image_url: str | None
551    "URL of the preview image extracted from the scraped page. Present on `scraped_link` type only. `null` otherwise."
552    image_width: int | None
553    "Width in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise."
554    key: str | None
555    'For `structured_data` type, what the data is about as set by the sender (for example `"pr:14963"`), copied from the record so it can be read without `object`. Not unique. `null` when unset and on other types.'
556    media_type: str | None
557    'The media category, e.g. `"video"` or `"audio"`. Present on `media` type only; omitted otherwise.'
558    name: str | None
559    "Display name of the media item. Present on `media` type only. `null` otherwise."
560    object: dict[str, Any] | None
561    "The full embedded object payload. For `task` type, contains the task record. For `action` type, contains the action definition. For `chart` type, contains the chart with its inline `spec`. For `structured_data` type, contains the inline `data` and the `schema_url` or `schema_config` naming its JSON Schema. Omitted on other types."
562    title: str | None
563    "Display title. The page title for `scraped_link`, the artifact name for `artifact`, and the task title for `task` types. `null` on other types."
564    type: Required[str]
565    'The attachment type. One of `"file"`, `"scraped_link"`, `"artifact"`, `"task"`, `"media"`, `"action"`, `"chart"`, or `"structured_data"`. Determines which additional fields are present.'
566    url: str | None
567    "URL to access the resource. A signed download URL for `file` and `artifact` types; the original URL for `scraped_link`; a media playback URL for `media`. `null` on `task` and `action` types."
568    variants: list[MessageUpdatedPayloadMessageAttachmentsItemVariantsItem] | None
569    "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise."
570    version: int | None
571    "Version number of the attached artifact at the time of attachment. Present on `artifact` type only. `null` otherwise."
572    width: int | None
573    "Width in pixels of the media item. Present on `media` type only. `null` otherwise."
content_type: str | None

MIME type of the attached file, e.g. "image/png" or "application/pdf". Present on file, artifact, and media types. null otherwise.

description: str | None

Short description. The page meta-description for scraped_link, the artifact description for artifact, and the task description for task types. null on other types.

filename: str | None

Original filename of the attached file, e.g. "report.pdf". Present on file, artifact, and media types. null otherwise.

height: int | None

Height in pixels of the media item. Present on media type only. null otherwise.

id: Required[str]

Unique identifier for this attachment within the message.

image_height: int | None

Height in pixels of the scraped preview image. Present on scraped_link type only. null otherwise.

Image source metadata for inline rendering. Present on file, scraped_link, artifact, and media types when the content is an image. null otherwise.

image_url: str | None

URL of the preview image extracted from the scraped page. Present on scraped_link type only. null otherwise.

image_width: int | None

Width in pixels of the scraped preview image. Present on scraped_link type only. null otherwise.

key: str | None

For structured_data type, what the data is about as set by the sender (for example "pr:14963"), copied from the record so it can be read without object. Not unique. null when unset and on other types.

media_type: str | None

The media category, e.g. "video" or "audio". Present on media type only; omitted otherwise.

name: str | None

Display name of the media item. Present on media type only. null otherwise.

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

The full embedded object payload. For task type, contains the task record. For action type, contains the action definition. For chart type, contains the chart with its inline spec. For structured_data type, contains the inline data and the schema_url or schema_config naming its JSON Schema. Omitted on other types.

title: str | None

Display title. The page title for scraped_link, the artifact name for artifact, and the task title for task types. null on other types.

type: Required[str]

The attachment type. One of "file", "scraped_link", "artifact", "task", "media", "action", "chart", or "structured_data". Determines which additional fields are present.

url: str | None

URL to access the resource. A signed download URL for file and artifact types; the original URL for scraped_link; a media playback URL for media. null on task and action types.

Array of available encoding variants for the media item (e.g. different resolutions). Present on media type only; omitted otherwise.

version: int | None

Version number of the attached artifact at the time of attachment. Present on artifact type only. null otherwise.

width: int | None

Width in pixels of the media item. Present on media type only. null otherwise.

class MessageUpdatedPayloadMessageContextItem(typing.TypedDict):
576class MessageUpdatedPayloadMessageContextItem(TypedDict, total=False):
577    attributes: dict[str, Any] | None
578    "Scalar key-value fields describing the context, such as route, repository, or pull-request number."
579    content: str | None
580    "Optional context body. The model receives it as escaped XML data, not a system instruction."
581    title: str | None
582    "Optional human-readable label for this context block."
583    type: Required[str]
584    "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores."
attributes: dict[str, typing.Any] | None

Scalar key-value fields describing the context, such as route, repository, or pull-request number.

content: str | None

Optional context body. The model receives it as escaped XML data, not a system instruction.

title: str | None

Optional human-readable label for this context block.

type: Required[str]

Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.

class MessageUpdatedPayloadMessageReactionsItem(typing.TypedDict):
587class MessageUpdatedPayloadMessageReactionsItem(TypedDict, total=False):
588    payload: dict[str, Any] | None
589    'Type-specific reaction data. For `"emoji_reaction"` reactions, contains an `emoji` key with the Unicode emoji string (e.g., `" "`).'
590    type: Required[str]
591    'Reaction type identifier. Currently always `"emoji_reaction"` for emoji-based reactions.'
592    user: str | None
593    "Public ID of the user who added the reaction (`usr_...`)."
payload: dict[str, typing.Any] | None

Type-specific reaction data. For "emoji_reaction" reactions, contains an emoji key with the Unicode emoji string (e.g., " ").

type: Required[str]

Reaction type identifier. Currently always "emoji_reaction" for emoji-based reactions.

user: str | None

Public ID of the user who added the reaction (usr_...).

class MessageUpdatedPayloadMessage(typing.TypedDict):
596class MessageUpdatedPayloadMessage(TypedDict, total=False):
597    acl: MessageUpdatedPayloadMessageAcl | None
598    "Access control list for private messages (grants with `read` action). Only returned to resource owners (and privileged/org-admin viewers) via server-side `field_redactions: [acl: :owner]`; `null` for everyone else."
599    actors: list[MessageUpdatedPayloadMessageActorsItem] | None
600    "Resolved actor descriptors for the message sender, combining identity and display metadata. Always contains exactly one entry."
601    agent: str | None
602    "ID of the agent user that sent this message (`agi_...`). `null` for messages sent by human users."
603    agent_mode: Literal["cli", "embedded"] | None
604    "Local agent execution mode for this message. One of `cli`, `embedded`, or `null` when the message was not created by a local agent execution path."
605    attachments: list[MessageUpdatedPayloadMessageAttachmentsItem] | None
606    "Files, links, tasks, media, artifacts, and actions attached to this message. Empty array if there are no attachments."
607    branched_thread: str | None
608    "ID of the thread that was branched from this message (`thr_...`). `null` if this message has not spawned a branch thread."
609    content: str | None
610    "Text content of the message. `null` for messages that contain only attachments."
611    context: list[MessageUpdatedPayloadMessageContextItem] | None
612    "Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions."
613    created_at: str | None
614    "When the message was posted (ISO 8601)."
615    has_replies: bool | None
616    "Whether this message has at least one reply. Only present when explicitly requested or computed by the server."
617    id: Required[str]
618    "Message ID (`msg_...`)."
619    idempotency_key: str | None
620    "Client-supplied idempotency key used to deduplicate message sends. `null` if the sender did not provide one."
621    is_deleted: bool | None
622    "Whether this message is a deletion tombstone. `true` only on the `message_updated` broadcast emitted when a message is deleted: the original content is replaced with a placeholder and the message no longer exists on the server. Always `false` for live messages."
623    legacy_agent: str | None
624    "Identifier of the legacy chat agent that sent this message, if applicable. `null` for messages sent by users or modern agent users."
625    metadata: dict[str, Any] | None
626    "Arbitrary key-value metadata attached to the message. Always present; defaults to an empty object when no metadata has been set."
627    org: str | None
628    "ID of the organization that owns this message (`org_...`)."
629    reactions: list[MessageUpdatedPayloadMessageReactionsItem] | None
630    "Emoji and other reactions added to this message by users. Empty array if no reactions have been added or the association is not preloaded."
631    rendering_mode: str | None
632    'Display hint for how the message should be rendered. One of `"reply"`, `"direct"`, or `"inline"`. `null` for user-authored messages, which are always rendered as standard replies.'
633    replies: list[dict[str, Any]] | None
634    "Inline array of reply messages, each serialized as a full message object. Only present when the server has preloaded replies for this message."
635    replies_after_cursor: str | None
636    "Opaque pagination cursor to fetch replies posted after the current page. Only present when inline replies are included in the response."
637    replies_before_cursor: str | None
638    "Opaque pagination cursor to fetch replies posted before the current page. Only present when inline replies are included in the response."
639    reply_count: int | None
640    "Total number of direct replies to this message. Only present when explicitly requested or computed by the server."
641    reply_to: dict[str, Any] | None
642    "The parent message this message is a reply to, expanded as a full message object when loaded. `null` if this is a top-level message or the association is not preloaded."
643    root_message_id: str | None
644    "ID of the root message in this reply chain (`msg_...`). `null` for a top-level message. The value is persisted when the reply is created, so callers can correlate a multi-turn session without walking parent messages."
645    sandbox: str | None
646    "ID of the developer sandbox this message belongs to (`dsb_...`). `null` for non-sandbox messages."
647    team: str | None
648    "ID of the team this message is scoped to (`tem_...`). `null` if the message is not team-scoped."
649    thread: str | None
650    "ID of the thread this message belongs to (`thr_...`)."
651    type: str | None
652    "Optional client-defined classification for the message (for example `note` or `status`). Free-form string up to 64 characters. The value `system` is reserved for platform-authored messages and cannot be set by clients. `null` when unset."
653    user: str | dict[str, Any] | None
654    "The human user who sent this message. Returns a public ID string (`usr_...`) when the association is not preloaded, or an expanded user object when it is. `null` for messages sent by agents."
655    visibility: Literal["default", "private"] | None
656    "Message-level visibility. `default` is visible to anyone who can see the parent thread. `private` is restricted to the sender and explicit ACL `read` grantees."

Access control list for private messages (grants with read action). Only returned to resource owners (and privileged/org-admin viewers) via server-side field_redactions: [acl: :owner]; null for everyone else.

Resolved actor descriptors for the message sender, combining identity and display metadata. Always contains exactly one entry.

agent: str | None

ID of the agent user that sent this message (agi_...). null for messages sent by human users.

agent_mode: Optional[Literal['cli', 'embedded']]

Local agent execution mode for this message. One of cli, embedded, or null when the message was not created by a local agent execution path.

Files, links, tasks, media, artifacts, and actions attached to this message. Empty array if there are no attachments.

branched_thread: str | None

ID of the thread that was branched from this message (thr_...). null if this message has not spawned a branch thread.

content: str | None

Text content of the message. null for messages that contain only attachments.

Immutable structured context captured when the message was posted. Each entry has type, optional title and content, and scalar attributes. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions.

created_at: str | None

When the message was posted (ISO 8601).

has_replies: bool | None

Whether this message has at least one reply. Only present when explicitly requested or computed by the server.

id: Required[str]

Message ID (msg_...).

idempotency_key: str | None

Client-supplied idempotency key used to deduplicate message sends. null if the sender did not provide one.

is_deleted: bool | None

Whether this message is a deletion tombstone. true only on the message_updated broadcast emitted when a message is deleted: the original content is replaced with a placeholder and the message no longer exists on the server. Always false for live messages.

legacy_agent: str | None

Identifier of the legacy chat agent that sent this message, if applicable. null for messages sent by users or modern agent users.

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

Arbitrary key-value metadata attached to the message. Always present; defaults to an empty object when no metadata has been set.

org: str | None

ID of the organization that owns this message (org_...).

Emoji and other reactions added to this message by users. Empty array if no reactions have been added or the association is not preloaded.

rendering_mode: str | None

Display hint for how the message should be rendered. One of "reply", "direct", or "inline". null for user-authored messages, which are always rendered as standard replies.

replies: list[dict[str, typing.Any]] | None

Inline array of reply messages, each serialized as a full message object. Only present when the server has preloaded replies for this message.

replies_after_cursor: str | None

Opaque pagination cursor to fetch replies posted after the current page. Only present when inline replies are included in the response.

replies_before_cursor: str | None

Opaque pagination cursor to fetch replies posted before the current page. Only present when inline replies are included in the response.

reply_count: int | None

Total number of direct replies to this message. Only present when explicitly requested or computed by the server.

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

The parent message this message is a reply to, expanded as a full message object when loaded. null if this is a top-level message or the association is not preloaded.

root_message_id: str | None

ID of the root message in this reply chain (msg_...). null for a top-level message. The value is persisted when the reply is created, so callers can correlate a multi-turn session without walking parent messages.

sandbox: str | None

ID of the developer sandbox this message belongs to (dsb_...). null for non-sandbox messages.

team: str | None

ID of the team this message is scoped to (tem_...). null if the message is not team-scoped.

thread: str | None

ID of the thread this message belongs to (thr_...).

type: str | None

Optional client-defined classification for the message (for example note or status). Free-form string up to 64 characters. The value system is reserved for platform-authored messages and cannot be set by clients. null when unset.

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

The human user who sent this message. Returns a public ID string (usr_...) when the association is not preloaded, or an expanded user object when it is. null for messages sent by agents.

visibility: Optional[Literal['default', 'private']]

Message-level visibility. default is visible to anyone who can see the parent thread. private is restricted to the sender and explicit ACL read grantees.

class MessageUpdatedPayload(typing.TypedDict):
659class MessageUpdatedPayload(TypedDict, total=False):
660    "Broadcast when a message is updated or removed"
661
662    message: MessageUpdatedPayloadMessage | None
663    "A chat message posted in a thread, including its content, author, attachments, reactions, and optional reply metadata."
664    thread_id: str | None

Broadcast when a message is updated or removed

A chat message posted in a thread, including its content, author, attachments, reactions, and optional reply metadata.

thread_id: str | None
class ThreadEventPayload(typing.TypedDict):
667class ThreadEventPayload(TypedDict, total=False):
668    "Broadcast thread-level events (agent updates, read state, unread counts)"
669
670    payload: dict[str, Any] | None
671    thread_id: str | None
672    type: str | None

Broadcast thread-level events (agent updates, read state, unread counts)

payload: dict[str, typing.Any] | None
thread_id: str | None
type: str | None
class SystemEventPayload(typing.TypedDict):
675class SystemEventPayload(TypedDict, total=False):
676    "Broadcast system-wide events"
677
678    event: dict[str, Any] | None

Broadcast system-wide events

event: dict[str, typing.Any] | None
class TypingPayloadActorProfilePicture(typing.TypedDict):
681class TypingPayloadActorProfilePicture(TypedDict, total=False):
682    file: str | None
683    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
684    height: int | None
685    "Height of the image in pixels. `null` if not known."
686    media: str | None
687    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
688    mime_type: str | None
689    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
690    refresh_url: str | None
691    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
692    url: str | None
693    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
694    width: int | None
695    "Width of the image in pixels. `null` if not known."
file: str | None

ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.

height: int | None

Height of the image in pixels. null if not known.

media: str | None

ID of the associated media record (med_...). null when the image is not linked to a media entity.

mime_type: str | None

MIME type of the image, e.g. "image/png" or "image/jpeg". null if not known.

refresh_url: str | None

Endpoint URL you can call to obtain a fresh signed url when the current one has expired. null if the URL does not require refreshing.

url: str | None

Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.

width: int | None

Width of the image in pixels. null if not known.

class TypingPayloadActor(typing.TypedDict):
698class TypingPayloadActor(TypedDict, total=False):
699    alias: str | None
700    "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured."
701    id: str | None
702    'Composite actor identifier. Format is `"user-<usr_...>"` for human users or `"agent-<agi_...>"` for agents.'
703    name: str | None
704    "Display name of the actor shown in the UI. `null` if no name is set."
705    profile_picture: TypingPayloadActorProfilePicture | None
706    "Profile picture for the actor. `null` if the actor has no profile picture."
alias: str | None

Short handle or alias for the actor, used as an alternate display identifier. null if not configured.

id: str | None

Composite actor identifier. Format is "user-<usr_...>" for human users or "agent-<agi_...>" for agents.

name: str | None

Display name of the actor shown in the UI. null if no name is set.

profile_picture: TypingPayloadActorProfilePicture | None

Profile picture for the actor. null if the actor has no profile picture.

class TypingPayload(typing.TypedDict):
709class TypingPayload(TypedDict, total=False):
710    "Broadcast when a participant (human or agent) starts or stops typing. Ephemeral; never persisted."
711
712    actor: TypingPayloadActor | None
713    "The entity that authored a message, either a human user or an agent."
714    is_typing: bool | None
715    thread_id: str | None

Broadcast when a participant (human or agent) starts or stops typing. Ephemeral; never persisted.

actor: TypingPayloadActor | None

The entity that authored a message, either a human user or an agent.

is_typing: bool | None
thread_id: str | None
class LocalToolCallPayloadRequestCallsItem(typing.TypedDict):
718class LocalToolCallPayloadRequestCallsItem(TypedDict):
719    arguments: dict[str, Any]
720    id: str
721    name: str
arguments: dict[str, typing.Any]
id: str
name: str
class LocalToolCallPayloadRequest(typing.TypedDict):
724class LocalToolCallPayloadRequest(TypedDict):
725    calls: list[LocalToolCallPayloadRequestCallsItem]
726    id: str
id: str
class LocalToolCallPayload(typing.TypedDict):
729class LocalToolCallPayload(TypedDict, total=False):
730    "Invoke local functions on the exact connection that published them"
731
732    agent_id: str | None
733    expires_at: datetime | None
734    generation: int | None
735    provider_id: str | None
736    request: LocalToolCallPayloadRequest | None
737    "A correlated batch of local function calls targeted to one connection."
738    thread_id: str | None

Invoke local functions on the exact connection that published them

agent_id: str | None
expires_at: datetime.datetime | None
generation: int | None
provider_id: str | None
request: LocalToolCallPayloadRequest | None

A correlated batch of local function calls targeted to one connection.

thread_id: str | None
class LocalToolCancelledPayload(typing.TypedDict):
741class LocalToolCancelledPayload(TypedDict, total=False):
742    "Stop local work because the server no longer accepts its result"
743
744    generation: int | None
745    provider_id: str | None
746    reason: str | None
747    request_id: str | None

Stop local work because the server no longer accepts its result

generation: int | None
provider_id: str | None
reason: str | None
request_id: str | None
class ApiChatChannel:
 754class ApiChatChannel:
 755    def __init__(self, channel, join_response=None):
 756        self._channel = channel
 757        self.join_response = join_response
 758
 759    # Join a team-scoped thread by ID
 760    @staticmethod
 761    def topic_team_thread(team_id: str, thread_id: str) -> str:
 762        return f"api:chat:team:{team_id}:thread:{thread_id}"
 763
 764    # Join a team-scoped thread by ID
 765    @classmethod
 766    async def join_team_thread(
 767        cls,
 768        socket: "Socket",
 769        team_id: str,
 770        thread_id: str,
 771        *,
 772        after_cursor: str | None = None,
 773        before_cursor: str | None = None,
 774        include_metadata: bool | None = None,
 775        limit: int | None = None,
 776    ) -> "ApiChatChannel":
 777        topic = cls.topic_team_thread(team_id, thread_id)
 778        channel = socket.channel(topic)
 779        payload: dict[str, object] = {}
 780        if after_cursor is not None:
 781            payload["after_cursor"] = after_cursor
 782        if before_cursor is not None:
 783            payload["before_cursor"] = before_cursor
 784        if include_metadata is not None:
 785            payload["include_metadata"] = include_metadata
 786        if limit is not None:
 787            payload["limit"] = limit
 788        join_response = await channel.join(payload)
 789        return cls(channel, join_response)
 790
 791    # Join or create a team-scoped keyed thread
 792    @staticmethod
 793    def topic_team_keyed(team_id: str, key: str) -> str:
 794        return f"api:chat:team:{team_id}:key:{key}"
 795
 796    # Join or create a team-scoped keyed thread
 797    @classmethod
 798    async def join_team_keyed(
 799        cls,
 800        socket: "Socket",
 801        team_id: str,
 802        key: str,
 803        *,
 804        after_cursor: str | None = None,
 805        before_cursor: str | None = None,
 806        include_metadata: bool | None = None,
 807        limit: int | None = None,
 808    ) -> "ApiChatChannel":
 809        topic = cls.topic_team_keyed(team_id, key)
 810        channel = socket.channel(topic)
 811        payload: dict[str, object] = {}
 812        if after_cursor is not None:
 813            payload["after_cursor"] = after_cursor
 814        if before_cursor is not None:
 815            payload["before_cursor"] = before_cursor
 816        if include_metadata is not None:
 817            payload["include_metadata"] = include_metadata
 818        if limit is not None:
 819            payload["limit"] = limit
 820        join_response = await channel.join(payload)
 821        return cls(channel, join_response)
 822
 823    # Join a team-scoped transient (ephemeral) thread
 824    @staticmethod
 825    def topic_team_transient(team_id: str, key: str) -> str:
 826        return f"api:chat:team:{team_id}:transient:{key}"
 827
 828    # Join a team-scoped transient (ephemeral) thread
 829    @classmethod
 830    async def join_team_transient(
 831        cls,
 832        socket: "Socket",
 833        team_id: str,
 834        key: str,
 835        *,
 836        after_cursor: str | None = None,
 837        before_cursor: str | None = None,
 838        include_metadata: bool | None = None,
 839        limit: int | None = None,
 840    ) -> "ApiChatChannel":
 841        topic = cls.topic_team_transient(team_id, key)
 842        channel = socket.channel(topic)
 843        payload: dict[str, object] = {}
 844        if after_cursor is not None:
 845            payload["after_cursor"] = after_cursor
 846        if before_cursor is not None:
 847            payload["before_cursor"] = before_cursor
 848        if include_metadata is not None:
 849            payload["include_metadata"] = include_metadata
 850        if limit is not None:
 851            payload["limit"] = limit
 852        join_response = await channel.join(payload)
 853        return cls(channel, join_response)
 854
 855    # Join an organization-visibility thread by ID
 856    @staticmethod
 857    def topic_org_thread(org_id: str, thread_id: str) -> str:
 858        return f"api:chat:org:{org_id}:thread:{thread_id}"
 859
 860    # Join an organization-visibility thread by ID
 861    @classmethod
 862    async def join_org_thread(
 863        cls,
 864        socket: "Socket",
 865        org_id: str,
 866        thread_id: str,
 867        *,
 868        after_cursor: str | None = None,
 869        before_cursor: str | None = None,
 870        include_metadata: bool | None = None,
 871        limit: int | None = None,
 872    ) -> "ApiChatChannel":
 873        topic = cls.topic_org_thread(org_id, thread_id)
 874        channel = socket.channel(topic)
 875        payload: dict[str, object] = {}
 876        if after_cursor is not None:
 877            payload["after_cursor"] = after_cursor
 878        if before_cursor is not None:
 879            payload["before_cursor"] = before_cursor
 880        if include_metadata is not None:
 881            payload["include_metadata"] = include_metadata
 882        if limit is not None:
 883            payload["limit"] = limit
 884        join_response = await channel.join(payload)
 885        return cls(channel, join_response)
 886
 887    # Join a user-scoped thread by ID, optionally supplying local tools for a personal thread
 888    @staticmethod
 889    def topic_user_thread(thread_id: str) -> str:
 890        return f"api:chat:user:thread:{thread_id}"
 891
 892    # Join a user-scoped thread by ID, optionally supplying local tools for a personal thread
 893    @classmethod
 894    async def join_user_thread(
 895        cls,
 896        socket: "Socket",
 897        thread_id: str,
 898        *,
 899        after_cursor: str | None = None,
 900        before_cursor: str | None = None,
 901        include_metadata: bool | None = None,
 902        limit: int | None = None,
 903        local_tool_provider_id: str | None = None,
 904        local_tools: list[dict[str, Any]] | None = None,
 905    ) -> "ApiChatChannel":
 906        topic = cls.topic_user_thread(thread_id)
 907        channel = socket.channel(topic)
 908        payload: dict[str, object] = {}
 909        if after_cursor is not None:
 910            payload["after_cursor"] = after_cursor
 911        if before_cursor is not None:
 912            payload["before_cursor"] = before_cursor
 913        if include_metadata is not None:
 914            payload["include_metadata"] = include_metadata
 915        if limit is not None:
 916            payload["limit"] = limit
 917        if local_tool_provider_id is not None:
 918            payload["local_tool_provider_id"] = local_tool_provider_id
 919        if local_tools is not None:
 920            payload["local_tools"] = local_tools
 921        join_response = await channel.join(payload)
 922        return cls(channel, join_response)
 923
 924    # Join or create a user-scoped keyed thread
 925    @staticmethod
 926    def topic_user_keyed(key: str) -> str:
 927        return f"api:chat:user:key:{key}"
 928
 929    # Join or create a user-scoped keyed thread
 930    @classmethod
 931    async def join_user_keyed(
 932        cls,
 933        socket: "Socket",
 934        key: str,
 935        *,
 936        after_cursor: str | None = None,
 937        before_cursor: str | None = None,
 938        include_metadata: bool | None = None,
 939        limit: int | None = None,
 940    ) -> "ApiChatChannel":
 941        topic = cls.topic_user_keyed(key)
 942        channel = socket.channel(topic)
 943        payload: dict[str, object] = {}
 944        if after_cursor is not None:
 945            payload["after_cursor"] = after_cursor
 946        if before_cursor is not None:
 947            payload["before_cursor"] = before_cursor
 948        if include_metadata is not None:
 949            payload["include_metadata"] = include_metadata
 950        if limit is not None:
 951            payload["limit"] = limit
 952        join_response = await channel.join(payload)
 953        return cls(channel, join_response)
 954
 955    # Join a user-scoped transient (ephemeral) thread
 956    @staticmethod
 957    def topic_user_transient(key: str) -> str:
 958        return f"api:chat:user:transient:{key}"
 959
 960    # Join a user-scoped transient (ephemeral) thread
 961    @classmethod
 962    async def join_user_transient(
 963        cls,
 964        socket: "Socket",
 965        key: str,
 966        *,
 967        after_cursor: str | None = None,
 968        before_cursor: str | None = None,
 969        include_metadata: bool | None = None,
 970        limit: int | None = None,
 971    ) -> "ApiChatChannel":
 972        topic = cls.topic_user_transient(key)
 973        channel = socket.channel(topic)
 974        payload: dict[str, object] = {}
 975        if after_cursor is not None:
 976            payload["after_cursor"] = after_cursor
 977        if before_cursor is not None:
 978            payload["before_cursor"] = before_cursor
 979        if include_metadata is not None:
 980            payload["include_metadata"] = include_metadata
 981        if limit is not None:
 982            payload["limit"] = limit
 983        join_response = await channel.join(payload)
 984        return cls(channel, join_response)
 985
 986    # Leave the underlying channel.
 987    async def leave(self):
 988        await self._channel.leave()
 989
 990    # Fork a sub-thread from an existing message
 991    async def api_chat_fork_thread(self, payload: ApiChatForkThreadInput) -> dict[str, Any]:
 992        return await self._channel.push("api:chat:fork_thread", payload)
 993
 994    # Mark a thread as read up to a given message
 995    async def api_chat_mark_thread_read(
 996        self, payload: ApiChatMarkThreadReadInput
 997    ) -> dict[str, Any]:
 998        return await self._channel.push("api:chat:mark_thread_read", payload)
 999
1000    # List all messages in the current thread
1001    async def api_chat_list_messages(self, payload: dict) -> dict[str, Any]:
1002        return await self._channel.push("api:chat:list_messages", payload)
1003
1004    # Load additional messages with cursor-based pagination
1005    async def api_chat_load_more_messages(
1006        self, payload: ApiChatLoadMoreMessagesInput
1007    ) -> dict[str, Any]:
1008        return await self._channel.push("api:chat:load_more_messages", payload)
1009
1010    # Post a new message with optional uploads and reply-to
1011    async def api_chat_post_message(self, payload: ApiChatPostMessageInput) -> dict[str, Any]:
1012        return await self._channel.push("api:chat:post_message", payload)
1013
1014    # Post a simple text message
1015    async def api_chat_post_simple_message(
1016        self, payload: ApiChatPostSimpleMessageInput
1017    ) -> dict[str, Any]:
1018        return await self._channel.push("api:chat:post_simple_message", payload)
1019
1020    # Edit an existing message's content
1021    async def api_chat_edit_message(self, payload: ApiChatEditMessageInput) -> dict[str, Any]:
1022        return await self._channel.push("api:chat:edit_message", payload)
1023
1024    # Delete a message
1025    async def api_chat_delete_message(self, payload: ApiChatDeleteMessageInput) -> dict[str, Any]:
1026        return await self._channel.push("api:chat:delete_message", payload)
1027
1028    # Add an emoji reaction to a message
1029    async def api_chat_add_reaction(self, payload: ApiChatAddReactionInput) -> dict[str, Any]:
1030        return await self._channel.push("api:chat:add_reaction", payload)
1031
1032    # Remove an emoji reaction from a message
1033    async def api_chat_remove_reaction(self, payload: ApiChatRemoveReactionInput) -> dict[str, Any]:
1034        return await self._channel.push("api:chat:remove_reaction", payload)
1035
1036    # Signal that the current user has started or stopped typing in the thread
1037    async def api_chat_typing(self, payload: ApiChatTypingInput) -> dict[str, Any]:
1038        return await self._channel.push("api:chat:typing", payload)
1039
1040    # Replace the local tools advertised by this connection for later messages it posts
1041    async def api_chat_publish_local_tools(
1042        self, payload: ApiChatPublishLocalToolsInput
1043    ) -> dict[str, Any]:
1044        return await self._channel.push("api:chat:publish_local_tools", payload)
1045
1046    # Return one complete result batch for a connection-local tool request
1047    async def api_chat_local_tool_result(
1048        self, payload: ApiChatLocalToolResultInput
1049    ) -> dict[str, Any]:
1050        return await self._channel.push("api:chat:local_tool_result", payload)
1051
1052    # Broadcast when a new message is added to a thread
1053    def on_message_added(
1054        self, callback: Callable[[MessageAddedPayload], None]
1055    ) -> Callable[[], None]:
1056        return self._channel.on("message_added", callback)
1057
1058    # Broadcast when a message is updated or removed
1059    def on_message_updated(
1060        self, callback: Callable[[MessageUpdatedPayload], None]
1061    ) -> Callable[[], None]:
1062        return self._channel.on("message_updated", callback)
1063
1064    # Broadcast thread-level events (agent updates, read state, unread counts)
1065    def on_thread_event(self, callback: Callable[[ThreadEventPayload], None]) -> Callable[[], None]:
1066        return self._channel.on("thread_event", callback)
1067
1068    # Broadcast system-wide events
1069    def on_system_event(self, callback: Callable[[SystemEventPayload], None]) -> Callable[[], None]:
1070        return self._channel.on("system_event", callback)
1071
1072    # Broadcast when a participant (human or agent) starts or stops typing. Ephemeral; never persisted.
1073    def on_typing(self, callback: Callable[[TypingPayload], None]) -> Callable[[], None]:
1074        return self._channel.on("typing", callback)
1075
1076    # Invoke local functions on the exact connection that published them
1077    def on_local_tool_call(
1078        self, callback: Callable[[LocalToolCallPayload], None]
1079    ) -> Callable[[], None]:
1080        return self._channel.on("local_tool_call", callback)
1081
1082    # Stop local work because the server no longer accepts its result
1083    def on_local_tool_cancelled(
1084        self, callback: Callable[[LocalToolCancelledPayload], None]
1085    ) -> Callable[[], None]:
1086        return self._channel.on("local_tool_cancelled", callback)
ApiChatChannel(channel, join_response=None)
755    def __init__(self, channel, join_response=None):
756        self._channel = channel
757        self.join_response = join_response
join_response
@staticmethod
def topic_team_thread(team_id: str, thread_id: str) -> str:
760    @staticmethod
761    def topic_team_thread(team_id: str, thread_id: str) -> str:
762        return f"api:chat:team:{team_id}:thread:{thread_id}"
@classmethod
async def join_team_thread( cls, socket: archastro.phx_channel.Socket, team_id: str, thread_id: str, *, after_cursor: str | None = None, before_cursor: str | None = None, include_metadata: bool | None = None, limit: int | None = None) -> ApiChatChannel:
765    @classmethod
766    async def join_team_thread(
767        cls,
768        socket: "Socket",
769        team_id: str,
770        thread_id: str,
771        *,
772        after_cursor: str | None = None,
773        before_cursor: str | None = None,
774        include_metadata: bool | None = None,
775        limit: int | None = None,
776    ) -> "ApiChatChannel":
777        topic = cls.topic_team_thread(team_id, thread_id)
778        channel = socket.channel(topic)
779        payload: dict[str, object] = {}
780        if after_cursor is not None:
781            payload["after_cursor"] = after_cursor
782        if before_cursor is not None:
783            payload["before_cursor"] = before_cursor
784        if include_metadata is not None:
785            payload["include_metadata"] = include_metadata
786        if limit is not None:
787            payload["limit"] = limit
788        join_response = await channel.join(payload)
789        return cls(channel, join_response)
@staticmethod
def topic_team_keyed(team_id: str, key: str) -> str:
792    @staticmethod
793    def topic_team_keyed(team_id: str, key: str) -> str:
794        return f"api:chat:team:{team_id}:key:{key}"
@classmethod
async def join_team_keyed( cls, socket: archastro.phx_channel.Socket, team_id: str, key: str, *, after_cursor: str | None = None, before_cursor: str | None = None, include_metadata: bool | None = None, limit: int | None = None) -> ApiChatChannel:
797    @classmethod
798    async def join_team_keyed(
799        cls,
800        socket: "Socket",
801        team_id: str,
802        key: str,
803        *,
804        after_cursor: str | None = None,
805        before_cursor: str | None = None,
806        include_metadata: bool | None = None,
807        limit: int | None = None,
808    ) -> "ApiChatChannel":
809        topic = cls.topic_team_keyed(team_id, key)
810        channel = socket.channel(topic)
811        payload: dict[str, object] = {}
812        if after_cursor is not None:
813            payload["after_cursor"] = after_cursor
814        if before_cursor is not None:
815            payload["before_cursor"] = before_cursor
816        if include_metadata is not None:
817            payload["include_metadata"] = include_metadata
818        if limit is not None:
819            payload["limit"] = limit
820        join_response = await channel.join(payload)
821        return cls(channel, join_response)
@staticmethod
def topic_team_transient(team_id: str, key: str) -> str:
824    @staticmethod
825    def topic_team_transient(team_id: str, key: str) -> str:
826        return f"api:chat:team:{team_id}:transient:{key}"
@classmethod
async def join_team_transient( cls, socket: archastro.phx_channel.Socket, team_id: str, key: str, *, after_cursor: str | None = None, before_cursor: str | None = None, include_metadata: bool | None = None, limit: int | None = None) -> ApiChatChannel:
829    @classmethod
830    async def join_team_transient(
831        cls,
832        socket: "Socket",
833        team_id: str,
834        key: str,
835        *,
836        after_cursor: str | None = None,
837        before_cursor: str | None = None,
838        include_metadata: bool | None = None,
839        limit: int | None = None,
840    ) -> "ApiChatChannel":
841        topic = cls.topic_team_transient(team_id, key)
842        channel = socket.channel(topic)
843        payload: dict[str, object] = {}
844        if after_cursor is not None:
845            payload["after_cursor"] = after_cursor
846        if before_cursor is not None:
847            payload["before_cursor"] = before_cursor
848        if include_metadata is not None:
849            payload["include_metadata"] = include_metadata
850        if limit is not None:
851            payload["limit"] = limit
852        join_response = await channel.join(payload)
853        return cls(channel, join_response)
@staticmethod
def topic_org_thread(org_id: str, thread_id: str) -> str:
856    @staticmethod
857    def topic_org_thread(org_id: str, thread_id: str) -> str:
858        return f"api:chat:org:{org_id}:thread:{thread_id}"
@classmethod
async def join_org_thread( cls, socket: archastro.phx_channel.Socket, org_id: str, thread_id: str, *, after_cursor: str | None = None, before_cursor: str | None = None, include_metadata: bool | None = None, limit: int | None = None) -> ApiChatChannel:
861    @classmethod
862    async def join_org_thread(
863        cls,
864        socket: "Socket",
865        org_id: str,
866        thread_id: str,
867        *,
868        after_cursor: str | None = None,
869        before_cursor: str | None = None,
870        include_metadata: bool | None = None,
871        limit: int | None = None,
872    ) -> "ApiChatChannel":
873        topic = cls.topic_org_thread(org_id, thread_id)
874        channel = socket.channel(topic)
875        payload: dict[str, object] = {}
876        if after_cursor is not None:
877            payload["after_cursor"] = after_cursor
878        if before_cursor is not None:
879            payload["before_cursor"] = before_cursor
880        if include_metadata is not None:
881            payload["include_metadata"] = include_metadata
882        if limit is not None:
883            payload["limit"] = limit
884        join_response = await channel.join(payload)
885        return cls(channel, join_response)
@staticmethod
def topic_user_thread(thread_id: str) -> str:
888    @staticmethod
889    def topic_user_thread(thread_id: str) -> str:
890        return f"api:chat:user:thread:{thread_id}"
@classmethod
async def join_user_thread( cls, socket: archastro.phx_channel.Socket, thread_id: str, *, after_cursor: str | None = None, before_cursor: str | None = None, include_metadata: bool | None = None, limit: int | None = None, local_tool_provider_id: str | None = None, local_tools: list[dict[str, typing.Any]] | None = None) -> ApiChatChannel:
893    @classmethod
894    async def join_user_thread(
895        cls,
896        socket: "Socket",
897        thread_id: str,
898        *,
899        after_cursor: str | None = None,
900        before_cursor: str | None = None,
901        include_metadata: bool | None = None,
902        limit: int | None = None,
903        local_tool_provider_id: str | None = None,
904        local_tools: list[dict[str, Any]] | None = None,
905    ) -> "ApiChatChannel":
906        topic = cls.topic_user_thread(thread_id)
907        channel = socket.channel(topic)
908        payload: dict[str, object] = {}
909        if after_cursor is not None:
910            payload["after_cursor"] = after_cursor
911        if before_cursor is not None:
912            payload["before_cursor"] = before_cursor
913        if include_metadata is not None:
914            payload["include_metadata"] = include_metadata
915        if limit is not None:
916            payload["limit"] = limit
917        if local_tool_provider_id is not None:
918            payload["local_tool_provider_id"] = local_tool_provider_id
919        if local_tools is not None:
920            payload["local_tools"] = local_tools
921        join_response = await channel.join(payload)
922        return cls(channel, join_response)
@staticmethod
def topic_user_keyed(key: str) -> str:
925    @staticmethod
926    def topic_user_keyed(key: str) -> str:
927        return f"api:chat:user:key:{key}"
@classmethod
async def join_user_keyed( cls, socket: archastro.phx_channel.Socket, key: str, *, after_cursor: str | None = None, before_cursor: str | None = None, include_metadata: bool | None = None, limit: int | None = None) -> ApiChatChannel:
930    @classmethod
931    async def join_user_keyed(
932        cls,
933        socket: "Socket",
934        key: str,
935        *,
936        after_cursor: str | None = None,
937        before_cursor: str | None = None,
938        include_metadata: bool | None = None,
939        limit: int | None = None,
940    ) -> "ApiChatChannel":
941        topic = cls.topic_user_keyed(key)
942        channel = socket.channel(topic)
943        payload: dict[str, object] = {}
944        if after_cursor is not None:
945            payload["after_cursor"] = after_cursor
946        if before_cursor is not None:
947            payload["before_cursor"] = before_cursor
948        if include_metadata is not None:
949            payload["include_metadata"] = include_metadata
950        if limit is not None:
951            payload["limit"] = limit
952        join_response = await channel.join(payload)
953        return cls(channel, join_response)
@staticmethod
def topic_user_transient(key: str) -> str:
956    @staticmethod
957    def topic_user_transient(key: str) -> str:
958        return f"api:chat:user:transient:{key}"
@classmethod
async def join_user_transient( cls, socket: archastro.phx_channel.Socket, key: str, *, after_cursor: str | None = None, before_cursor: str | None = None, include_metadata: bool | None = None, limit: int | None = None) -> ApiChatChannel:
961    @classmethod
962    async def join_user_transient(
963        cls,
964        socket: "Socket",
965        key: str,
966        *,
967        after_cursor: str | None = None,
968        before_cursor: str | None = None,
969        include_metadata: bool | None = None,
970        limit: int | None = None,
971    ) -> "ApiChatChannel":
972        topic = cls.topic_user_transient(key)
973        channel = socket.channel(topic)
974        payload: dict[str, object] = {}
975        if after_cursor is not None:
976            payload["after_cursor"] = after_cursor
977        if before_cursor is not None:
978            payload["before_cursor"] = before_cursor
979        if include_metadata is not None:
980            payload["include_metadata"] = include_metadata
981        if limit is not None:
982            payload["limit"] = limit
983        join_response = await channel.join(payload)
984        return cls(channel, join_response)
async def leave(self):
987    async def leave(self):
988        await self._channel.leave()
async def api_chat_fork_thread( self, payload: ApiChatForkThreadInput) -> dict[str, typing.Any]:
991    async def api_chat_fork_thread(self, payload: ApiChatForkThreadInput) -> dict[str, Any]:
992        return await self._channel.push("api:chat:fork_thread", payload)
async def api_chat_mark_thread_read( self, payload: ApiChatMarkThreadReadInput) -> dict[str, typing.Any]:
995    async def api_chat_mark_thread_read(
996        self, payload: ApiChatMarkThreadReadInput
997    ) -> dict[str, Any]:
998        return await self._channel.push("api:chat:mark_thread_read", payload)
async def api_chat_list_messages(self, payload: dict) -> dict[str, typing.Any]:
1001    async def api_chat_list_messages(self, payload: dict) -> dict[str, Any]:
1002        return await self._channel.push("api:chat:list_messages", payload)
async def api_chat_load_more_messages( self, payload: ApiChatLoadMoreMessagesInput) -> dict[str, typing.Any]:
1005    async def api_chat_load_more_messages(
1006        self, payload: ApiChatLoadMoreMessagesInput
1007    ) -> dict[str, Any]:
1008        return await self._channel.push("api:chat:load_more_messages", payload)
async def api_chat_post_message( self, payload: ApiChatPostMessageInput) -> dict[str, typing.Any]:
1011    async def api_chat_post_message(self, payload: ApiChatPostMessageInput) -> dict[str, Any]:
1012        return await self._channel.push("api:chat:post_message", payload)
async def api_chat_post_simple_message( self, payload: ApiChatPostSimpleMessageInput) -> dict[str, typing.Any]:
1015    async def api_chat_post_simple_message(
1016        self, payload: ApiChatPostSimpleMessageInput
1017    ) -> dict[str, Any]:
1018        return await self._channel.push("api:chat:post_simple_message", payload)
async def api_chat_edit_message( self, payload: ApiChatEditMessageInput) -> dict[str, typing.Any]:
1021    async def api_chat_edit_message(self, payload: ApiChatEditMessageInput) -> dict[str, Any]:
1022        return await self._channel.push("api:chat:edit_message", payload)
async def api_chat_delete_message( self, payload: ApiChatDeleteMessageInput) -> dict[str, typing.Any]:
1025    async def api_chat_delete_message(self, payload: ApiChatDeleteMessageInput) -> dict[str, Any]:
1026        return await self._channel.push("api:chat:delete_message", payload)
async def api_chat_add_reaction( self, payload: ApiChatAddReactionInput) -> dict[str, typing.Any]:
1029    async def api_chat_add_reaction(self, payload: ApiChatAddReactionInput) -> dict[str, Any]:
1030        return await self._channel.push("api:chat:add_reaction", payload)
async def api_chat_remove_reaction( self, payload: ApiChatRemoveReactionInput) -> dict[str, typing.Any]:
1033    async def api_chat_remove_reaction(self, payload: ApiChatRemoveReactionInput) -> dict[str, Any]:
1034        return await self._channel.push("api:chat:remove_reaction", payload)
async def api_chat_typing( self, payload: ApiChatTypingInput) -> dict[str, typing.Any]:
1037    async def api_chat_typing(self, payload: ApiChatTypingInput) -> dict[str, Any]:
1038        return await self._channel.push("api:chat:typing", payload)
async def api_chat_publish_local_tools( self, payload: ApiChatPublishLocalToolsInput) -> dict[str, typing.Any]:
1041    async def api_chat_publish_local_tools(
1042        self, payload: ApiChatPublishLocalToolsInput
1043    ) -> dict[str, Any]:
1044        return await self._channel.push("api:chat:publish_local_tools", payload)
async def api_chat_local_tool_result( self, payload: ApiChatLocalToolResultInput) -> dict[str, typing.Any]:
1047    async def api_chat_local_tool_result(
1048        self, payload: ApiChatLocalToolResultInput
1049    ) -> dict[str, Any]:
1050        return await self._channel.push("api:chat:local_tool_result", payload)
def on_message_added( self, callback: Callable[[MessageAddedPayload], None]) -> Callable[[], None]:
1053    def on_message_added(
1054        self, callback: Callable[[MessageAddedPayload], None]
1055    ) -> Callable[[], None]:
1056        return self._channel.on("message_added", callback)
def on_message_updated( self, callback: Callable[[MessageUpdatedPayload], None]) -> Callable[[], None]:
1059    def on_message_updated(
1060        self, callback: Callable[[MessageUpdatedPayload], None]
1061    ) -> Callable[[], None]:
1062        return self._channel.on("message_updated", callback)
def on_thread_event( self, callback: Callable[[ThreadEventPayload], None]) -> Callable[[], None]:
1065    def on_thread_event(self, callback: Callable[[ThreadEventPayload], None]) -> Callable[[], None]:
1066        return self._channel.on("thread_event", callback)
def on_system_event( self, callback: Callable[[SystemEventPayload], None]) -> Callable[[], None]:
1069    def on_system_event(self, callback: Callable[[SystemEventPayload], None]) -> Callable[[], None]:
1070        return self._channel.on("system_event", callback)
def on_typing( self, callback: Callable[[TypingPayload], None]) -> Callable[[], None]:
1073    def on_typing(self, callback: Callable[[TypingPayload], None]) -> Callable[[], None]:
1074        return self._channel.on("typing", callback)
def on_local_tool_call( self, callback: Callable[[LocalToolCallPayload], None]) -> Callable[[], None]:
1077    def on_local_tool_call(
1078        self, callback: Callable[[LocalToolCallPayload], None]
1079    ) -> Callable[[], None]:
1080        return self._channel.on("local_tool_call", callback)
def on_local_tool_cancelled( self, callback: Callable[[LocalToolCancelledPayload], None]) -> Callable[[], None]:
1083    def on_local_tool_cancelled(
1084        self, callback: Callable[[LocalToolCancelledPayload], None]
1085    ) -> Callable[[], None]:
1086        return self._channel.on("local_tool_cancelled", callback)