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)
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
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
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
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."
Scalar key-value fields describing the context, such as route, repository, or pull-request number.
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
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."
Scalar key-value fields describing the context, such as route, repository, or pull-request number.
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
81class ApiChatEditMessageInput(TypedDict): 82 "Edit an existing message's content" 83 84 content: str 85 message_id: str
Edit an existing message's content
Delete a message
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
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
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
120class ApiChatPublishLocalToolsInputLocalToolsItem(TypedDict): 121 function: ApiChatPublishLocalToolsInputLocalToolsItemFunction 122 "A function implemented by the client connected to a personal thread." 123 type: Literal["function"]
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
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
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"`.'
Array of action strings the principal is permitted to perform, e.g. ["read", "write"]. Must contain at least one entry.
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".
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"`.'
Array of action strings the principal is permitted to perform, e.g. ["read", "write"]. Must contain at least one entry.
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".
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"`.'
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".
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.
Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with grants.
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."
ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.
ID of the associated media record (med_...). null when the image is not linked to a media entity.
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.
Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.
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."
Short handle or alias for the actor, used as an alternate display identifier. null if not configured.
Composite actor identifier. Format is "user-<usr_...>" for human users or "agent-<agi_...>" for agents.
Profile picture for the actor. null if the actor has no profile picture.
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."
ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.
ID of the associated media record (med_...). null when the image is not linked to a media entity.
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.
Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.
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."
ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.
ID of the associated media record (med_...). null when the image is not linked to a media entity.
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.
Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.
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."
MIME type of this variant's file (e.g., "image/jpeg", "video/mp4"). null if the file is not loaded.
Original filename of the uploaded file for this variant. null if the file is not loaded.
Resolved image delivery metadata for this variant, including dimensions and CDN URL. null for non-image content types.
Signed download URL for this variant, resolved at request time. null if the file is unavailable.
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."
MIME type of the attached file, e.g. "image/png" or "application/pdf". Present on file, artifact, and media types. null otherwise.
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.
Original filename of the attached file, e.g. "report.pdf". Present on file, artifact, and media types. null otherwise.
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.
URL of the preview image extracted from the scraped page. Present on scraped_link type only. null otherwise.
Width in pixels of the scraped preview image. Present on scraped_link type only. null otherwise.
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.
The media category, e.g. "video" or "audio". Present on media type only; omitted otherwise.
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.
Display title. The page title for scraped_link, the artifact name for artifact, and the task title for task types. null on other types.
The attachment type. One of "file", "scraped_link", "artifact", "task", "media", "action", "chart", or "structured_data". Determines which additional fields are present.
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.
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."
Scalar key-value fields describing the context, such as route, repository, or pull-request number.
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_...`)."
Type-specific reaction data. For "emoji_reaction" reactions, contains an emoji key with the Unicode emoji string (e.g., " ").
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.
ID of the agent user that sent this message (agi_...). null for messages sent by human users.
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.
ID of the thread that was branched from this message (thr_...). null if this message has not spawned a branch thread.
Whether this message has at least one reply. Only present when explicitly requested or computed by the server.
Client-supplied idempotency key used to deduplicate message sends. null if the sender did not provide one.
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.
Identifier of the legacy chat agent that sent this message, if applicable. null for messages sent by users or modern agent users.
Arbitrary key-value metadata attached to the message. Always present; defaults to an empty object when no metadata has been set.
Emoji and other reactions added to this message by users. Empty array if no reactions have been added or the association is not preloaded.
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.
Inline array of reply messages, each serialized as a full message object. Only present when the server has preloaded replies for this message.
Opaque pagination cursor to fetch replies posted after the current page. Only present when inline replies are included in the response.
Opaque pagination cursor to fetch replies posted before the current page. Only present when inline replies are included in the response.
Total number of direct replies to this message. Only present when explicitly requested or computed by the server.
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.
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.
ID of the developer sandbox this message belongs to (dsb_...). null for non-sandbox messages.
ID of the team this message is scoped to (tem_...). null if the message is not team-scoped.
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.
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
A chat message posted in a thread, including its content, author, attachments, reactions, and optional reply metadata.
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"`.'
Array of action strings the principal is permitted to perform, e.g. ["read", "write"]. Must contain at least one entry.
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".
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"`.'
Array of action strings the principal is permitted to perform, e.g. ["read", "write"]. Must contain at least one entry.
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".
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"`.'
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".
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.
Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with grants.
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."
ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.
ID of the associated media record (med_...). null when the image is not linked to a media entity.
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.
Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.
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."
Short handle or alias for the actor, used as an alternate display identifier. null if not configured.
Composite actor identifier. Format is "user-<usr_...>" for human users or "agent-<agi_...>" for agents.
Profile picture for the actor. null if the actor has no profile picture.
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."
ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.
ID of the associated media record (med_...). null when the image is not linked to a media entity.
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.
Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.
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."
ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.
ID of the associated media record (med_...). null when the image is not linked to a media entity.
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.
Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.
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."
MIME type of this variant's file (e.g., "image/jpeg", "video/mp4"). null if the file is not loaded.
Original filename of the uploaded file for this variant. null if the file is not loaded.
Resolved image delivery metadata for this variant, including dimensions and CDN URL. null for non-image content types.
Signed download URL for this variant, resolved at request time. null if the file is unavailable.
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."
MIME type of the attached file, e.g. "image/png" or "application/pdf". Present on file, artifact, and media types. null otherwise.
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.
Original filename of the attached file, e.g. "report.pdf". Present on file, artifact, and media types. null otherwise.
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.
URL of the preview image extracted from the scraped page. Present on scraped_link type only. null otherwise.
Width in pixels of the scraped preview image. Present on scraped_link type only. null otherwise.
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.
The media category, e.g. "video" or "audio". Present on media type only; omitted otherwise.
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.
Display title. The page title for scraped_link, the artifact name for artifact, and the task title for task types. null on other types.
The attachment type. One of "file", "scraped_link", "artifact", "task", "media", "action", "chart", or "structured_data". Determines which additional fields are present.
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.
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."
Scalar key-value fields describing the context, such as route, repository, or pull-request number.
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_...`)."
Type-specific reaction data. For "emoji_reaction" reactions, contains an emoji key with the Unicode emoji string (e.g., " ").
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.
ID of the agent user that sent this message (agi_...). null for messages sent by human users.
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.
ID of the thread that was branched from this message (thr_...). null if this message has not spawned a branch thread.
Whether this message has at least one reply. Only present when explicitly requested or computed by the server.
Client-supplied idempotency key used to deduplicate message sends. null if the sender did not provide one.
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.
Identifier of the legacy chat agent that sent this message, if applicable. null for messages sent by users or modern agent users.
Arbitrary key-value metadata attached to the message. Always present; defaults to an empty object when no metadata has been set.
Emoji and other reactions added to this message by users. Empty array if no reactions have been added or the association is not preloaded.
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.
Inline array of reply messages, each serialized as a full message object. Only present when the server has preloaded replies for this message.
Opaque pagination cursor to fetch replies posted after the current page. Only present when inline replies are included in the response.
Opaque pagination cursor to fetch replies posted before the current page. Only present when inline replies are included in the response.
Total number of direct replies to this message. Only present when explicitly requested or computed by the server.
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.
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.
ID of the developer sandbox this message belongs to (dsb_...). null for non-sandbox messages.
ID of the team this message is scoped to (tem_...). null if the message is not team-scoped.
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.
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.
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)
675class SystemEventPayload(TypedDict, total=False): 676 "Broadcast system-wide events" 677 678 event: dict[str, Any] | None
Broadcast system-wide events
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."
ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.
ID of the associated media record (med_...). null when the image is not linked to a media entity.
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.
Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.
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."
Short handle or alias for the actor, used as an alternate display identifier. null if not configured.
Composite actor identifier. Format is "user-<usr_...>" for human users or "agent-<agi_...>" for agents.
Profile picture for the actor. null if the actor has no profile picture.
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.
The entity that authored a message, either a human user or an agent.
724class LocalToolCallPayloadRequest(TypedDict): 725 calls: list[LocalToolCallPayloadRequestCallsItem] 726 id: str
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
A correlated batch of local function calls targeted to one connection.
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
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)
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)
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)
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)
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)
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)
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)
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)