archastro.platform.channels.api_chat_channel
1# Copyright (c) 2026 ArchAstro Inc. All Rights Reserved. 2# This file is auto-generated by @archastro/sdk-generator. Do not edit. 3# Content hash: 25ed606eeb04 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 ApiChatPostMessageInput(TypedDict, total=False): 36 "Post a new message with optional uploads and reply-to" 37 38 content: Required[str] 39 idempotency_key: str 40 reply_to: str 41 uploads: list[dict[str, Any]] 42 43 44class ApiChatPostSimpleMessageInput(TypedDict, total=False): 45 "Post a simple text message" 46 47 content: str 48 idempotency_key: str 49 reply_to: str 50 51 52class ApiChatEditMessageInput(TypedDict): 53 "Edit an existing message's content" 54 55 content: str 56 message_id: str 57 58 59class ApiChatDeleteMessageInput(TypedDict): 60 "Delete a message" 61 62 message_id: str 63 64 65class ApiChatAddReactionInput(TypedDict): 66 "Add an emoji reaction to a message" 67 68 emoji: str 69 message_id: str 70 71 72class ApiChatRemoveReactionInput(TypedDict): 73 "Remove an emoji reaction from a message" 74 75 emoji: str 76 message_id: str 77 78 79class ApiChatTypingInput(TypedDict): 80 "Signal that the current user has started or stopped typing in the thread" 81 82 is_typing: bool 83 84 85class MessageAddedPayloadMessageActorsItemProfilePicture(TypedDict, total=False): 86 file: str | None 87 "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file." 88 height: int | None 89 "Height of the image in pixels. `null` if not known." 90 media: str | None 91 "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity." 92 mime_type: str | None 93 'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.' 94 refresh_url: str | None 95 "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." 96 url: str | None 97 "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires." 98 width: int | None 99 "Width of the image in pixels. `null` if not known." 100 101 102class MessageAddedPayloadMessageActorsItem(TypedDict, total=False): 103 alias: str | None 104 "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured." 105 id: str | None 106 'Composite actor identifier. Format is `"user-<usr_...>"` for human users or `"agent-<agi_...>"` for agents.' 107 name: str | None 108 "Display name of the actor shown in the UI. `null` if no name is set." 109 profile_picture: MessageAddedPayloadMessageActorsItemProfilePicture | None 110 "Profile picture for the actor. `null` if the actor has no profile picture." 111 112 113class MessageAddedPayloadMessageAttachmentsItemImageSource(TypedDict, total=False): 114 file: str | None 115 "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file." 116 height: int | None 117 "Height of the image in pixels. `null` if not known." 118 media: str | None 119 "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity." 120 mime_type: str | None 121 'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.' 122 refresh_url: str | None 123 "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." 124 url: str | None 125 "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires." 126 width: int | None 127 "Width of the image in pixels. `null` if not known." 128 129 130class MessageAddedPayloadMessageAttachmentsItemVariantsItemImageSource(TypedDict, total=False): 131 file: str | None 132 "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file." 133 height: int | None 134 "Height of the image in pixels. `null` if not known." 135 media: str | None 136 "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity." 137 mime_type: str | None 138 'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.' 139 refresh_url: str | None 140 "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." 141 url: str | None 142 "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires." 143 width: int | None 144 "Width of the image in pixels. `null` if not known." 145 146 147class MessageAddedPayloadMessageAttachmentsItemVariantsItem(TypedDict, total=False): 148 content_type: str | None 149 'MIME type of this variant\'s file (e.g., `"image/jpeg"`, `"video/mp4"`). `null` if the file is not loaded.' 150 created_at: datetime | None 151 "When this variant was created (ISO 8601)." 152 file: str | None 153 "ID of the underlying storage file that backs this variant (`fil_...`)." 154 filename: str | None 155 "Original filename of the uploaded file for this variant. `null` if the file is not loaded." 156 height: int | None 157 "Height of this variant in pixels. `null` if not recorded." 158 id: Required[str] 159 "Media variant ID (`mvr_...`)." 160 image_source: MessageAddedPayloadMessageAttachmentsItemVariantsItemImageSource | None 161 "Resolved image delivery metadata for this variant, including dimensions and CDN URL. `null` for non-image content types." 162 updated_at: datetime | None 163 "When this variant was last updated (ISO 8601)." 164 url: str | None 165 "Signed download URL for this variant, resolved at request time. `null` if the file is unavailable." 166 variant_key: str | None 167 'Identifier for this variant\'s processing tier. Common values include `"original"` (the unmodified upload) and `"thumbnail"` (a resized preview).' 168 width: int | None 169 "Width of this variant in pixels. `null` if not recorded." 170 171 172class MessageAddedPayloadMessageAttachmentsItem(TypedDict, total=False): 173 content_type: str | None 174 'MIME type of the attached file, e.g. `"image/png"` or `"application/pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.' 175 description: str | None 176 "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." 177 filename: str | None 178 'Original filename of the attached file, e.g. `"report.pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.' 179 height: int | None 180 "Height in pixels of the media item. Present on `media` type only. `null` otherwise." 181 id: Required[str] 182 "Unique identifier for this attachment within the message." 183 image_height: int | None 184 "Height in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise." 185 image_source: MessageAddedPayloadMessageAttachmentsItemImageSource | None 186 "Image source metadata for inline rendering. Present on `file`, `scraped_link`, `artifact`, and `media` types when the content is an image. `null` otherwise." 187 image_url: str | None 188 "URL of the preview image extracted from the scraped page. Present on `scraped_link` type only. `null` otherwise." 189 image_width: int | None 190 "Width in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise." 191 media_type: str | None 192 'The media category, e.g. `"video"` or `"audio"`. Present on `media` type only. `null` otherwise.' 193 name: str | None 194 "Display name of the media item. Present on `media` type only. `null` otherwise." 195 object: dict[str, Any] | None 196 "The full embedded object payload. For `task` type, contains the task record. For `action` type, contains the action definition. `null` on other types." 197 title: str | None 198 "Display title. The page title for `scraped_link`, the artifact name for `artifact`, and the task title for `task` types. `null` on other types." 199 type: Required[str] 200 'The attachment type. One of `"file"`, `"scraped_link"`, `"artifact"`, `"task"`, `"media"`, or `"action"`. Determines which additional fields are present.' 201 url: str | None 202 "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." 203 variants: list[MessageAddedPayloadMessageAttachmentsItemVariantsItem] | None 204 "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only. `null` otherwise." 205 version: int | None 206 "Version number of the attached artifact at the time of attachment. Present on `artifact` type only. `null` otherwise." 207 width: int | None 208 "Width in pixels of the media item. Present on `media` type only. `null` otherwise." 209 210 211class MessageAddedPayloadMessageReactionsItem(TypedDict, total=False): 212 payload: dict[str, Any] | None 213 'Type-specific reaction data. For `"emoji_reaction"` reactions, contains an `emoji` key with the Unicode emoji string (e.g., `" "`).' 214 type: Required[str] 215 'Reaction type identifier. Currently always `"emoji_reaction"` for emoji-based reactions.' 216 user: str | None 217 "Public ID of the user who added the reaction (`usr_...`)." 218 219 220class MessageAddedPayloadMessage(TypedDict, total=False): 221 actors: list[MessageAddedPayloadMessageActorsItem] | None 222 "Resolved actor descriptors for the message sender, combining identity and display metadata. Always contains exactly one entry." 223 agent: str | None 224 "ID of the agent user that sent this message (`agi_...`). `null` for messages sent by human users." 225 agent_mode: Literal["cli", "embedded"] | None 226 "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." 227 attachments: list[MessageAddedPayloadMessageAttachmentsItem] | None 228 "Files, links, tasks, media, artifacts, and actions attached to this message. Empty array if there are no attachments." 229 branched_thread: str | None 230 "ID of the thread that was branched from this message (`thr_...`). `null` if this message has not spawned a branch thread." 231 content: str | None 232 "Text content of the message. `null` for messages that contain only attachments." 233 created_at: datetime | None 234 "When the message was posted (ISO 8601)." 235 has_replies: bool | None 236 "Whether this message has at least one reply. Only present when explicitly requested or computed by the server." 237 id: Required[str] 238 "Message ID (`msg_...`)." 239 idempotency_key: str | None 240 "Client-supplied idempotency key used to deduplicate message sends. `null` if the sender did not provide one." 241 legacy_agent: str | None 242 "Identifier of the legacy chat agent that sent this message, if applicable. `null` for messages sent by users or modern agent users." 243 metadata: dict[str, Any] | None 244 "Arbitrary key-value metadata attached to the message. Always present; defaults to an empty object when no metadata has been set." 245 org: str | None 246 "ID of the organization that owns this message (`org_...`)." 247 reactions: list[MessageAddedPayloadMessageReactionsItem] | None 248 "Emoji and other reactions added to this message by users. Empty array if no reactions have been added or the association is not preloaded." 249 rendering_mode: str | None 250 '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.' 251 replies: list[dict[str, Any]] | None 252 "Inline array of reply messages, each serialized as a full message object. Only present when the server has preloaded replies for this message." 253 replies_after_cursor: str | None 254 "Opaque pagination cursor to fetch replies posted after the current page. Only present when inline replies are included in the response." 255 replies_before_cursor: str | None 256 "Opaque pagination cursor to fetch replies posted before the current page. Only present when inline replies are included in the response." 257 reply_count: int | None 258 "Total number of direct replies to this message. Only present when explicitly requested or computed by the server." 259 reply_to: dict[str, Any] | None 260 "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." 261 sandbox: str | None 262 "ID of the developer sandbox this message belongs to (`dsb_...`). `null` for non-sandbox messages." 263 team: str | None 264 "ID of the team this message is scoped to (`tem_...`). `null` if the message is not team-scoped." 265 thread: str | None 266 "ID of the thread this message belongs to (`thr_...`). `null` for messages not yet associated with a thread." 267 user: str | None 268 "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." 269 270 271class MessageAddedPayload(TypedDict, total=False): 272 "Broadcast when a new message is added to a thread" 273 274 after_cursor: str | None 275 before_cursor: str | None 276 message: MessageAddedPayloadMessage | None 277 "A chat message posted in a thread, including its content, author, attachments, reactions, and optional reply metadata." 278 thread_id: str | None 279 280 281class MessageUpdatedPayloadMessageActorsItemProfilePicture(TypedDict, total=False): 282 file: str | None 283 "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file." 284 height: int | None 285 "Height of the image in pixels. `null` if not known." 286 media: str | None 287 "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity." 288 mime_type: str | None 289 'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.' 290 refresh_url: str | None 291 "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." 292 url: str | None 293 "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires." 294 width: int | None 295 "Width of the image in pixels. `null` if not known." 296 297 298class MessageUpdatedPayloadMessageActorsItem(TypedDict, total=False): 299 alias: str | None 300 "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured." 301 id: str | None 302 'Composite actor identifier. Format is `"user-<usr_...>"` for human users or `"agent-<agi_...>"` for agents.' 303 name: str | None 304 "Display name of the actor shown in the UI. `null` if no name is set." 305 profile_picture: MessageUpdatedPayloadMessageActorsItemProfilePicture | None 306 "Profile picture for the actor. `null` if the actor has no profile picture." 307 308 309class MessageUpdatedPayloadMessageAttachmentsItemImageSource(TypedDict, total=False): 310 file: str | None 311 "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file." 312 height: int | None 313 "Height of the image in pixels. `null` if not known." 314 media: str | None 315 "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity." 316 mime_type: str | None 317 'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.' 318 refresh_url: str | None 319 "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." 320 url: str | None 321 "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires." 322 width: int | None 323 "Width of the image in pixels. `null` if not known." 324 325 326class MessageUpdatedPayloadMessageAttachmentsItemVariantsItemImageSource(TypedDict, total=False): 327 file: str | None 328 "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file." 329 height: int | None 330 "Height of the image in pixels. `null` if not known." 331 media: str | None 332 "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity." 333 mime_type: str | None 334 'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.' 335 refresh_url: str | None 336 "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." 337 url: str | None 338 "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires." 339 width: int | None 340 "Width of the image in pixels. `null` if not known." 341 342 343class MessageUpdatedPayloadMessageAttachmentsItemVariantsItem(TypedDict, total=False): 344 content_type: str | None 345 'MIME type of this variant\'s file (e.g., `"image/jpeg"`, `"video/mp4"`). `null` if the file is not loaded.' 346 created_at: datetime | None 347 "When this variant was created (ISO 8601)." 348 file: str | None 349 "ID of the underlying storage file that backs this variant (`fil_...`)." 350 filename: str | None 351 "Original filename of the uploaded file for this variant. `null` if the file is not loaded." 352 height: int | None 353 "Height of this variant in pixels. `null` if not recorded." 354 id: Required[str] 355 "Media variant ID (`mvr_...`)." 356 image_source: MessageUpdatedPayloadMessageAttachmentsItemVariantsItemImageSource | None 357 "Resolved image delivery metadata for this variant, including dimensions and CDN URL. `null` for non-image content types." 358 updated_at: datetime | None 359 "When this variant was last updated (ISO 8601)." 360 url: str | None 361 "Signed download URL for this variant, resolved at request time. `null` if the file is unavailable." 362 variant_key: str | None 363 'Identifier for this variant\'s processing tier. Common values include `"original"` (the unmodified upload) and `"thumbnail"` (a resized preview).' 364 width: int | None 365 "Width of this variant in pixels. `null` if not recorded." 366 367 368class MessageUpdatedPayloadMessageAttachmentsItem(TypedDict, total=False): 369 content_type: str | None 370 'MIME type of the attached file, e.g. `"image/png"` or `"application/pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.' 371 description: str | None 372 "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." 373 filename: str | None 374 'Original filename of the attached file, e.g. `"report.pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.' 375 height: int | None 376 "Height in pixels of the media item. Present on `media` type only. `null` otherwise." 377 id: Required[str] 378 "Unique identifier for this attachment within the message." 379 image_height: int | None 380 "Height in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise." 381 image_source: MessageUpdatedPayloadMessageAttachmentsItemImageSource | None 382 "Image source metadata for inline rendering. Present on `file`, `scraped_link`, `artifact`, and `media` types when the content is an image. `null` otherwise." 383 image_url: str | None 384 "URL of the preview image extracted from the scraped page. Present on `scraped_link` type only. `null` otherwise." 385 image_width: int | None 386 "Width in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise." 387 media_type: str | None 388 'The media category, e.g. `"video"` or `"audio"`. Present on `media` type only. `null` otherwise.' 389 name: str | None 390 "Display name of the media item. Present on `media` type only. `null` otherwise." 391 object: dict[str, Any] | None 392 "The full embedded object payload. For `task` type, contains the task record. For `action` type, contains the action definition. `null` on other types." 393 title: str | None 394 "Display title. The page title for `scraped_link`, the artifact name for `artifact`, and the task title for `task` types. `null` on other types." 395 type: Required[str] 396 'The attachment type. One of `"file"`, `"scraped_link"`, `"artifact"`, `"task"`, `"media"`, or `"action"`. Determines which additional fields are present.' 397 url: str | None 398 "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." 399 variants: list[MessageUpdatedPayloadMessageAttachmentsItemVariantsItem] | None 400 "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only. `null` otherwise." 401 version: int | None 402 "Version number of the attached artifact at the time of attachment. Present on `artifact` type only. `null` otherwise." 403 width: int | None 404 "Width in pixels of the media item. Present on `media` type only. `null` otherwise." 405 406 407class MessageUpdatedPayloadMessageReactionsItem(TypedDict, total=False): 408 payload: dict[str, Any] | None 409 'Type-specific reaction data. For `"emoji_reaction"` reactions, contains an `emoji` key with the Unicode emoji string (e.g., `" "`).' 410 type: Required[str] 411 'Reaction type identifier. Currently always `"emoji_reaction"` for emoji-based reactions.' 412 user: str | None 413 "Public ID of the user who added the reaction (`usr_...`)." 414 415 416class MessageUpdatedPayloadMessage(TypedDict, total=False): 417 actors: list[MessageUpdatedPayloadMessageActorsItem] | None 418 "Resolved actor descriptors for the message sender, combining identity and display metadata. Always contains exactly one entry." 419 agent: str | None 420 "ID of the agent user that sent this message (`agi_...`). `null` for messages sent by human users." 421 agent_mode: Literal["cli", "embedded"] | None 422 "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." 423 attachments: list[MessageUpdatedPayloadMessageAttachmentsItem] | None 424 "Files, links, tasks, media, artifacts, and actions attached to this message. Empty array if there are no attachments." 425 branched_thread: str | None 426 "ID of the thread that was branched from this message (`thr_...`). `null` if this message has not spawned a branch thread." 427 content: str | None 428 "Text content of the message. `null` for messages that contain only attachments." 429 created_at: datetime | None 430 "When the message was posted (ISO 8601)." 431 has_replies: bool | None 432 "Whether this message has at least one reply. Only present when explicitly requested or computed by the server." 433 id: Required[str] 434 "Message ID (`msg_...`)." 435 idempotency_key: str | None 436 "Client-supplied idempotency key used to deduplicate message sends. `null` if the sender did not provide one." 437 legacy_agent: str | None 438 "Identifier of the legacy chat agent that sent this message, if applicable. `null` for messages sent by users or modern agent users." 439 metadata: dict[str, Any] | None 440 "Arbitrary key-value metadata attached to the message. Always present; defaults to an empty object when no metadata has been set." 441 org: str | None 442 "ID of the organization that owns this message (`org_...`)." 443 reactions: list[MessageUpdatedPayloadMessageReactionsItem] | None 444 "Emoji and other reactions added to this message by users. Empty array if no reactions have been added or the association is not preloaded." 445 rendering_mode: str | None 446 '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.' 447 replies: list[dict[str, Any]] | None 448 "Inline array of reply messages, each serialized as a full message object. Only present when the server has preloaded replies for this message." 449 replies_after_cursor: str | None 450 "Opaque pagination cursor to fetch replies posted after the current page. Only present when inline replies are included in the response." 451 replies_before_cursor: str | None 452 "Opaque pagination cursor to fetch replies posted before the current page. Only present when inline replies are included in the response." 453 reply_count: int | None 454 "Total number of direct replies to this message. Only present when explicitly requested or computed by the server." 455 reply_to: dict[str, Any] | None 456 "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." 457 sandbox: str | None 458 "ID of the developer sandbox this message belongs to (`dsb_...`). `null` for non-sandbox messages." 459 team: str | None 460 "ID of the team this message is scoped to (`tem_...`). `null` if the message is not team-scoped." 461 thread: str | None 462 "ID of the thread this message belongs to (`thr_...`). `null` for messages not yet associated with a thread." 463 user: str | None 464 "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." 465 466 467class MessageUpdatedPayload(TypedDict, total=False): 468 "Broadcast when a message is updated or removed" 469 470 message: MessageUpdatedPayloadMessage | None 471 "A chat message posted in a thread, including its content, author, attachments, reactions, and optional reply metadata." 472 thread_id: str | None 473 474 475class ThreadEventPayload(TypedDict, total=False): 476 "Broadcast thread-level events (agent updates, read state, unread counts)" 477 478 payload: dict[str, Any] | None 479 thread_id: str | None 480 type: str | None 481 482 483class SystemEventPayload(TypedDict, total=False): 484 "Broadcast system-wide events" 485 486 event: dict[str, Any] | None 487 488 489class TypingPayloadActorProfilePicture(TypedDict, total=False): 490 file: str | None 491 "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file." 492 height: int | None 493 "Height of the image in pixels. `null` if not known." 494 media: str | None 495 "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity." 496 mime_type: str | None 497 'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.' 498 refresh_url: str | None 499 "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." 500 url: str | None 501 "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires." 502 width: int | None 503 "Width of the image in pixels. `null` if not known." 504 505 506class TypingPayloadActor(TypedDict, total=False): 507 alias: str | None 508 "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured." 509 id: str | None 510 'Composite actor identifier. Format is `"user-<usr_...>"` for human users or `"agent-<agi_...>"` for agents.' 511 name: str | None 512 "Display name of the actor shown in the UI. `null` if no name is set." 513 profile_picture: TypingPayloadActorProfilePicture | None 514 "Profile picture for the actor. `null` if the actor has no profile picture." 515 516 517class TypingPayload(TypedDict, total=False): 518 "Broadcast when a participant (human or agent) starts or stops typing. Ephemeral; never persisted." 519 520 actor: TypingPayloadActor | None 521 "The entity that authored a message, either a human user or an agent." 522 is_typing: bool | None 523 thread_id: str | None 524 525 526# Channel for real-time chat messaging. 527# Supports team-scoped and user-scoped threads with keyed, transient, and direct 528# thread access patterns. 529class ApiChatChannel: 530 def __init__(self, channel, join_response=None): 531 self._channel = channel 532 self.join_response = join_response 533 534 # Join a team-scoped thread by ID 535 @staticmethod 536 def topic_team_thread(team_id: str, thread_id: str) -> str: 537 return f"api:chat:team:{team_id}:thread:{thread_id}" 538 539 # Join a team-scoped thread by ID 540 @classmethod 541 async def join_team_thread( 542 cls, 543 socket: "Socket", 544 team_id: str, 545 thread_id: str, 546 *, 547 after_cursor: str | None = None, 548 before_cursor: str | None = None, 549 include_metadata: bool | None = None, 550 limit: int | None = None, 551 ) -> "ApiChatChannel": 552 topic = cls.topic_team_thread(team_id, thread_id) 553 channel = socket.channel(topic) 554 payload: dict[str, object] = {} 555 if after_cursor is not None: 556 payload["after_cursor"] = after_cursor 557 if before_cursor is not None: 558 payload["before_cursor"] = before_cursor 559 if include_metadata is not None: 560 payload["include_metadata"] = include_metadata 561 if limit is not None: 562 payload["limit"] = limit 563 join_response = await channel.join(payload) 564 return cls(channel, join_response) 565 566 # Join or create a team-scoped keyed thread 567 @staticmethod 568 def topic_team_keyed(team_id: str, key: str) -> str: 569 return f"api:chat:team:{team_id}:key:{key}" 570 571 # Join or create a team-scoped keyed thread 572 @classmethod 573 async def join_team_keyed( 574 cls, 575 socket: "Socket", 576 team_id: str, 577 key: str, 578 *, 579 after_cursor: str | None = None, 580 before_cursor: str | None = None, 581 include_metadata: bool | None = None, 582 limit: int | None = None, 583 ) -> "ApiChatChannel": 584 topic = cls.topic_team_keyed(team_id, key) 585 channel = socket.channel(topic) 586 payload: dict[str, object] = {} 587 if after_cursor is not None: 588 payload["after_cursor"] = after_cursor 589 if before_cursor is not None: 590 payload["before_cursor"] = before_cursor 591 if include_metadata is not None: 592 payload["include_metadata"] = include_metadata 593 if limit is not None: 594 payload["limit"] = limit 595 join_response = await channel.join(payload) 596 return cls(channel, join_response) 597 598 # Join a team-scoped transient (ephemeral) thread 599 @staticmethod 600 def topic_team_transient(team_id: str, key: str) -> str: 601 return f"api:chat:team:{team_id}:transient:{key}" 602 603 # Join a team-scoped transient (ephemeral) thread 604 @classmethod 605 async def join_team_transient( 606 cls, 607 socket: "Socket", 608 team_id: str, 609 key: str, 610 *, 611 after_cursor: str | None = None, 612 before_cursor: str | None = None, 613 include_metadata: bool | None = None, 614 limit: int | None = None, 615 ) -> "ApiChatChannel": 616 topic = cls.topic_team_transient(team_id, key) 617 channel = socket.channel(topic) 618 payload: dict[str, object] = {} 619 if after_cursor is not None: 620 payload["after_cursor"] = after_cursor 621 if before_cursor is not None: 622 payload["before_cursor"] = before_cursor 623 if include_metadata is not None: 624 payload["include_metadata"] = include_metadata 625 if limit is not None: 626 payload["limit"] = limit 627 join_response = await channel.join(payload) 628 return cls(channel, join_response) 629 630 # Join a user-scoped thread by ID 631 @staticmethod 632 def topic_user_thread(thread_id: str) -> str: 633 return f"api:chat:user:thread:{thread_id}" 634 635 # Join a user-scoped thread by ID 636 @classmethod 637 async def join_user_thread( 638 cls, 639 socket: "Socket", 640 thread_id: str, 641 *, 642 after_cursor: str | None = None, 643 before_cursor: str | None = None, 644 include_metadata: bool | None = None, 645 limit: int | None = None, 646 ) -> "ApiChatChannel": 647 topic = cls.topic_user_thread(thread_id) 648 channel = socket.channel(topic) 649 payload: dict[str, object] = {} 650 if after_cursor is not None: 651 payload["after_cursor"] = after_cursor 652 if before_cursor is not None: 653 payload["before_cursor"] = before_cursor 654 if include_metadata is not None: 655 payload["include_metadata"] = include_metadata 656 if limit is not None: 657 payload["limit"] = limit 658 join_response = await channel.join(payload) 659 return cls(channel, join_response) 660 661 # Join or create a user-scoped keyed thread 662 @staticmethod 663 def topic_user_keyed(key: str) -> str: 664 return f"api:chat:user:key:{key}" 665 666 # Join or create a user-scoped keyed thread 667 @classmethod 668 async def join_user_keyed( 669 cls, 670 socket: "Socket", 671 key: str, 672 *, 673 after_cursor: str | None = None, 674 before_cursor: str | None = None, 675 include_metadata: bool | None = None, 676 limit: int | None = None, 677 ) -> "ApiChatChannel": 678 topic = cls.topic_user_keyed(key) 679 channel = socket.channel(topic) 680 payload: dict[str, object] = {} 681 if after_cursor is not None: 682 payload["after_cursor"] = after_cursor 683 if before_cursor is not None: 684 payload["before_cursor"] = before_cursor 685 if include_metadata is not None: 686 payload["include_metadata"] = include_metadata 687 if limit is not None: 688 payload["limit"] = limit 689 join_response = await channel.join(payload) 690 return cls(channel, join_response) 691 692 # Join a user-scoped transient (ephemeral) thread 693 @staticmethod 694 def topic_user_transient(key: str) -> str: 695 return f"api:chat:user:transient:{key}" 696 697 # Join a user-scoped transient (ephemeral) thread 698 @classmethod 699 async def join_user_transient( 700 cls, 701 socket: "Socket", 702 key: str, 703 *, 704 after_cursor: str | None = None, 705 before_cursor: str | None = None, 706 include_metadata: bool | None = None, 707 limit: int | None = None, 708 ) -> "ApiChatChannel": 709 topic = cls.topic_user_transient(key) 710 channel = socket.channel(topic) 711 payload: dict[str, object] = {} 712 if after_cursor is not None: 713 payload["after_cursor"] = after_cursor 714 if before_cursor is not None: 715 payload["before_cursor"] = before_cursor 716 if include_metadata is not None: 717 payload["include_metadata"] = include_metadata 718 if limit is not None: 719 payload["limit"] = limit 720 join_response = await channel.join(payload) 721 return cls(channel, join_response) 722 723 # Leave the underlying channel. 724 async def leave(self): 725 await self._channel.leave() 726 727 # Fork a sub-thread from an existing message 728 async def api_chat_fork_thread(self, payload: ApiChatForkThreadInput) -> dict[str, Any]: 729 return await self._channel.push("api:chat:fork_thread", payload) 730 731 # Mark a thread as read up to a given message 732 async def api_chat_mark_thread_read( 733 self, payload: ApiChatMarkThreadReadInput 734 ) -> dict[str, Any]: 735 return await self._channel.push("api:chat:mark_thread_read", payload) 736 737 # List all messages in the current thread 738 async def api_chat_list_messages(self, payload: dict) -> dict[str, Any]: 739 return await self._channel.push("api:chat:list_messages", payload) 740 741 # Load additional messages with cursor-based pagination 742 async def api_chat_load_more_messages( 743 self, payload: ApiChatLoadMoreMessagesInput 744 ) -> dict[str, Any]: 745 return await self._channel.push("api:chat:load_more_messages", payload) 746 747 # Post a new message with optional uploads and reply-to 748 async def api_chat_post_message(self, payload: ApiChatPostMessageInput) -> dict[str, Any]: 749 return await self._channel.push("api:chat:post_message", payload) 750 751 # Post a simple text message 752 async def api_chat_post_simple_message( 753 self, payload: ApiChatPostSimpleMessageInput 754 ) -> dict[str, Any]: 755 return await self._channel.push("api:chat:post_simple_message", payload) 756 757 # Edit an existing message's content 758 async def api_chat_edit_message(self, payload: ApiChatEditMessageInput) -> dict[str, Any]: 759 return await self._channel.push("api:chat:edit_message", payload) 760 761 # Delete a message 762 async def api_chat_delete_message(self, payload: ApiChatDeleteMessageInput) -> dict[str, Any]: 763 return await self._channel.push("api:chat:delete_message", payload) 764 765 # Add an emoji reaction to a message 766 async def api_chat_add_reaction(self, payload: ApiChatAddReactionInput) -> dict[str, Any]: 767 return await self._channel.push("api:chat:add_reaction", payload) 768 769 # Remove an emoji reaction from a message 770 async def api_chat_remove_reaction(self, payload: ApiChatRemoveReactionInput) -> dict[str, Any]: 771 return await self._channel.push("api:chat:remove_reaction", payload) 772 773 # Signal that the current user has started or stopped typing in the thread 774 async def api_chat_typing(self, payload: ApiChatTypingInput) -> dict[str, Any]: 775 return await self._channel.push("api:chat:typing", payload) 776 777 # Broadcast when a new message is added to a thread 778 def on_message_added( 779 self, callback: Callable[[MessageAddedPayload], None] 780 ) -> Callable[[], None]: 781 return self._channel.on("message_added", callback) 782 783 # Broadcast when a message is updated or removed 784 def on_message_updated( 785 self, callback: Callable[[MessageUpdatedPayload], None] 786 ) -> Callable[[], None]: 787 return self._channel.on("message_updated", callback) 788 789 # Broadcast thread-level events (agent updates, read state, unread counts) 790 def on_thread_event(self, callback: Callable[[ThreadEventPayload], None]) -> Callable[[], None]: 791 return self._channel.on("thread_event", callback) 792 793 # Broadcast system-wide events 794 def on_system_event(self, callback: Callable[[SystemEventPayload], None]) -> Callable[[], None]: 795 return self._channel.on("system_event", callback) 796 797 # Broadcast when a participant (human or agent) starts or stops typing. Ephemeral; never persisted. 798 def on_typing(self, callback: Callable[[TypingPayload], None]) -> Callable[[], None]: 799 return self._channel.on("typing", 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 ApiChatPostMessageInput(TypedDict, total=False): 37 "Post a new message with optional uploads and reply-to" 38 39 content: Required[str] 40 idempotency_key: str 41 reply_to: str 42 uploads: list[dict[str, Any]]
Post a new message with optional uploads and reply-to
45class ApiChatPostSimpleMessageInput(TypedDict, total=False): 46 "Post a simple text message" 47 48 content: str 49 idempotency_key: str 50 reply_to: str
Post a simple text message
53class ApiChatEditMessageInput(TypedDict): 54 "Edit an existing message's content" 55 56 content: str 57 message_id: str
Edit an existing message's content
Delete a message
66class ApiChatAddReactionInput(TypedDict): 67 "Add an emoji reaction to a message" 68 69 emoji: str 70 message_id: str
Add an emoji reaction to a message
73class ApiChatRemoveReactionInput(TypedDict): 74 "Remove an emoji reaction from a message" 75 76 emoji: str 77 message_id: str
Remove an emoji reaction from a message
80class ApiChatTypingInput(TypedDict): 81 "Signal that the current user has started or stopped typing in the thread" 82 83 is_typing: bool
Signal that the current user has started or stopped typing in the thread
86class MessageAddedPayloadMessageActorsItemProfilePicture(TypedDict, total=False): 87 file: str | None 88 "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file." 89 height: int | None 90 "Height of the image in pixels. `null` if not known." 91 media: str | None 92 "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity." 93 mime_type: str | None 94 'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.' 95 refresh_url: str | None 96 "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." 97 url: str | None 98 "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires." 99 width: int | None 100 "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.
103class MessageAddedPayloadMessageActorsItem(TypedDict, total=False): 104 alias: str | None 105 "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured." 106 id: str | None 107 'Composite actor identifier. Format is `"user-<usr_...>"` for human users or `"agent-<agi_...>"` for agents.' 108 name: str | None 109 "Display name of the actor shown in the UI. `null` if no name is set." 110 profile_picture: MessageAddedPayloadMessageActorsItemProfilePicture | None 111 "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.
114class MessageAddedPayloadMessageAttachmentsItemImageSource(TypedDict, total=False): 115 file: str | None 116 "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file." 117 height: int | None 118 "Height of the image in pixels. `null` if not known." 119 media: str | None 120 "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity." 121 mime_type: str | None 122 'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.' 123 refresh_url: str | None 124 "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." 125 url: str | None 126 "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires." 127 width: int | None 128 "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.
131class MessageAddedPayloadMessageAttachmentsItemVariantsItemImageSource(TypedDict, total=False): 132 file: str | None 133 "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file." 134 height: int | None 135 "Height of the image in pixels. `null` if not known." 136 media: str | None 137 "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity." 138 mime_type: str | None 139 'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.' 140 refresh_url: str | None 141 "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." 142 url: str | None 143 "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires." 144 width: int | None 145 "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.
148class MessageAddedPayloadMessageAttachmentsItemVariantsItem(TypedDict, total=False): 149 content_type: str | None 150 'MIME type of this variant\'s file (e.g., `"image/jpeg"`, `"video/mp4"`). `null` if the file is not loaded.' 151 created_at: datetime | None 152 "When this variant was created (ISO 8601)." 153 file: str | None 154 "ID of the underlying storage file that backs this variant (`fil_...`)." 155 filename: str | None 156 "Original filename of the uploaded file for this variant. `null` if the file is not loaded." 157 height: int | None 158 "Height of this variant in pixels. `null` if not recorded." 159 id: Required[str] 160 "Media variant ID (`mvr_...`)." 161 image_source: MessageAddedPayloadMessageAttachmentsItemVariantsItemImageSource | None 162 "Resolved image delivery metadata for this variant, including dimensions and CDN URL. `null` for non-image content types." 163 updated_at: datetime | None 164 "When this variant was last updated (ISO 8601)." 165 url: str | None 166 "Signed download URL for this variant, resolved at request time. `null` if the file is unavailable." 167 variant_key: str | None 168 'Identifier for this variant\'s processing tier. Common values include `"original"` (the unmodified upload) and `"thumbnail"` (a resized preview).' 169 width: int | None 170 "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.
173class MessageAddedPayloadMessageAttachmentsItem(TypedDict, total=False): 174 content_type: str | None 175 'MIME type of the attached file, e.g. `"image/png"` or `"application/pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.' 176 description: str | None 177 "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." 178 filename: str | None 179 'Original filename of the attached file, e.g. `"report.pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.' 180 height: int | None 181 "Height in pixels of the media item. Present on `media` type only. `null` otherwise." 182 id: Required[str] 183 "Unique identifier for this attachment within the message." 184 image_height: int | None 185 "Height in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise." 186 image_source: MessageAddedPayloadMessageAttachmentsItemImageSource | None 187 "Image source metadata for inline rendering. Present on `file`, `scraped_link`, `artifact`, and `media` types when the content is an image. `null` otherwise." 188 image_url: str | None 189 "URL of the preview image extracted from the scraped page. Present on `scraped_link` type only. `null` otherwise." 190 image_width: int | None 191 "Width in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise." 192 media_type: str | None 193 'The media category, e.g. `"video"` or `"audio"`. Present on `media` type only. `null` otherwise.' 194 name: str | None 195 "Display name of the media item. Present on `media` type only. `null` otherwise." 196 object: dict[str, Any] | None 197 "The full embedded object payload. For `task` type, contains the task record. For `action` type, contains the action definition. `null` on other types." 198 title: str | None 199 "Display title. The page title for `scraped_link`, the artifact name for `artifact`, and the task title for `task` types. `null` on other types." 200 type: Required[str] 201 'The attachment type. One of `"file"`, `"scraped_link"`, `"artifact"`, `"task"`, `"media"`, or `"action"`. Determines which additional fields are present.' 202 url: str | None 203 "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." 204 variants: list[MessageAddedPayloadMessageAttachmentsItemVariantsItem] | None 205 "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only. `null` otherwise." 206 version: int | None 207 "Version number of the attached artifact at the time of attachment. Present on `artifact` type only. `null` otherwise." 208 width: int | None 209 "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.
The media category, e.g. "video" or "audio". Present on media type only. null otherwise.
The full embedded object payload. For task type, contains the task record. For action type, contains the action definition. null 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", or "action". 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. null otherwise.
212class MessageAddedPayloadMessageReactionsItem(TypedDict, total=False): 213 payload: dict[str, Any] | None 214 'Type-specific reaction data. For `"emoji_reaction"` reactions, contains an `emoji` key with the Unicode emoji string (e.g., `" "`).' 215 type: Required[str] 216 'Reaction type identifier. Currently always `"emoji_reaction"` for emoji-based reactions.' 217 user: str | None 218 "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., " ").
221class MessageAddedPayloadMessage(TypedDict, total=False): 222 actors: list[MessageAddedPayloadMessageActorsItem] | None 223 "Resolved actor descriptors for the message sender, combining identity and display metadata. Always contains exactly one entry." 224 agent: str | None 225 "ID of the agent user that sent this message (`agi_...`). `null` for messages sent by human users." 226 agent_mode: Literal["cli", "embedded"] | None 227 "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." 228 attachments: list[MessageAddedPayloadMessageAttachmentsItem] | None 229 "Files, links, tasks, media, artifacts, and actions attached to this message. Empty array if there are no attachments." 230 branched_thread: str | None 231 "ID of the thread that was branched from this message (`thr_...`). `null` if this message has not spawned a branch thread." 232 content: str | None 233 "Text content of the message. `null` for messages that contain only attachments." 234 created_at: datetime | None 235 "When the message was posted (ISO 8601)." 236 has_replies: bool | None 237 "Whether this message has at least one reply. Only present when explicitly requested or computed by the server." 238 id: Required[str] 239 "Message ID (`msg_...`)." 240 idempotency_key: str | None 241 "Client-supplied idempotency key used to deduplicate message sends. `null` if the sender did not provide one." 242 legacy_agent: str | None 243 "Identifier of the legacy chat agent that sent this message, if applicable. `null` for messages sent by users or modern agent users." 244 metadata: dict[str, Any] | None 245 "Arbitrary key-value metadata attached to the message. Always present; defaults to an empty object when no metadata has been set." 246 org: str | None 247 "ID of the organization that owns this message (`org_...`)." 248 reactions: list[MessageAddedPayloadMessageReactionsItem] | None 249 "Emoji and other reactions added to this message by users. Empty array if no reactions have been added or the association is not preloaded." 250 rendering_mode: str | None 251 '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.' 252 replies: list[dict[str, Any]] | None 253 "Inline array of reply messages, each serialized as a full message object. Only present when the server has preloaded replies for this message." 254 replies_after_cursor: str | None 255 "Opaque pagination cursor to fetch replies posted after the current page. Only present when inline replies are included in the response." 256 replies_before_cursor: str | None 257 "Opaque pagination cursor to fetch replies posted before the current page. Only present when inline replies are included in the response." 258 reply_count: int | None 259 "Total number of direct replies to this message. Only present when explicitly requested or computed by the server." 260 reply_to: dict[str, Any] | None 261 "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." 262 sandbox: str | None 263 "ID of the developer sandbox this message belongs to (`dsb_...`). `null` for non-sandbox messages." 264 team: str | None 265 "ID of the team this message is scoped to (`tem_...`). `null` if the message is not team-scoped." 266 thread: str | None 267 "ID of the thread this message belongs to (`thr_...`). `null` for messages not yet associated with a thread." 268 user: str | None 269 "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."
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.
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 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.
272class MessageAddedPayload(TypedDict, total=False): 273 "Broadcast when a new message is added to a thread" 274 275 after_cursor: str | None 276 before_cursor: str | None 277 message: MessageAddedPayloadMessage | None 278 "A chat message posted in a thread, including its content, author, attachments, reactions, and optional reply metadata." 279 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.
282class MessageUpdatedPayloadMessageActorsItemProfilePicture(TypedDict, total=False): 283 file: str | None 284 "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file." 285 height: int | None 286 "Height of the image in pixels. `null` if not known." 287 media: str | None 288 "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity." 289 mime_type: str | None 290 'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.' 291 refresh_url: str | None 292 "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." 293 url: str | None 294 "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires." 295 width: int | None 296 "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.
299class MessageUpdatedPayloadMessageActorsItem(TypedDict, total=False): 300 alias: str | None 301 "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured." 302 id: str | None 303 'Composite actor identifier. Format is `"user-<usr_...>"` for human users or `"agent-<agi_...>"` for agents.' 304 name: str | None 305 "Display name of the actor shown in the UI. `null` if no name is set." 306 profile_picture: MessageUpdatedPayloadMessageActorsItemProfilePicture | None 307 "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.
310class MessageUpdatedPayloadMessageAttachmentsItemImageSource(TypedDict, total=False): 311 file: str | None 312 "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file." 313 height: int | None 314 "Height of the image in pixels. `null` if not known." 315 media: str | None 316 "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity." 317 mime_type: str | None 318 'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.' 319 refresh_url: str | None 320 "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." 321 url: str | None 322 "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires." 323 width: int | None 324 "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.
327class MessageUpdatedPayloadMessageAttachmentsItemVariantsItemImageSource(TypedDict, total=False): 328 file: str | None 329 "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file." 330 height: int | None 331 "Height of the image in pixels. `null` if not known." 332 media: str | None 333 "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity." 334 mime_type: str | None 335 'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.' 336 refresh_url: str | None 337 "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." 338 url: str | None 339 "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires." 340 width: int | None 341 "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.
344class MessageUpdatedPayloadMessageAttachmentsItemVariantsItem(TypedDict, total=False): 345 content_type: str | None 346 'MIME type of this variant\'s file (e.g., `"image/jpeg"`, `"video/mp4"`). `null` if the file is not loaded.' 347 created_at: datetime | None 348 "When this variant was created (ISO 8601)." 349 file: str | None 350 "ID of the underlying storage file that backs this variant (`fil_...`)." 351 filename: str | None 352 "Original filename of the uploaded file for this variant. `null` if the file is not loaded." 353 height: int | None 354 "Height of this variant in pixels. `null` if not recorded." 355 id: Required[str] 356 "Media variant ID (`mvr_...`)." 357 image_source: MessageUpdatedPayloadMessageAttachmentsItemVariantsItemImageSource | None 358 "Resolved image delivery metadata for this variant, including dimensions and CDN URL. `null` for non-image content types." 359 updated_at: datetime | None 360 "When this variant was last updated (ISO 8601)." 361 url: str | None 362 "Signed download URL for this variant, resolved at request time. `null` if the file is unavailable." 363 variant_key: str | None 364 'Identifier for this variant\'s processing tier. Common values include `"original"` (the unmodified upload) and `"thumbnail"` (a resized preview).' 365 width: int | None 366 "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.
369class MessageUpdatedPayloadMessageAttachmentsItem(TypedDict, total=False): 370 content_type: str | None 371 'MIME type of the attached file, e.g. `"image/png"` or `"application/pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.' 372 description: str | None 373 "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." 374 filename: str | None 375 'Original filename of the attached file, e.g. `"report.pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.' 376 height: int | None 377 "Height in pixels of the media item. Present on `media` type only. `null` otherwise." 378 id: Required[str] 379 "Unique identifier for this attachment within the message." 380 image_height: int | None 381 "Height in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise." 382 image_source: MessageUpdatedPayloadMessageAttachmentsItemImageSource | None 383 "Image source metadata for inline rendering. Present on `file`, `scraped_link`, `artifact`, and `media` types when the content is an image. `null` otherwise." 384 image_url: str | None 385 "URL of the preview image extracted from the scraped page. Present on `scraped_link` type only. `null` otherwise." 386 image_width: int | None 387 "Width in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise." 388 media_type: str | None 389 'The media category, e.g. `"video"` or `"audio"`. Present on `media` type only. `null` otherwise.' 390 name: str | None 391 "Display name of the media item. Present on `media` type only. `null` otherwise." 392 object: dict[str, Any] | None 393 "The full embedded object payload. For `task` type, contains the task record. For `action` type, contains the action definition. `null` on other types." 394 title: str | None 395 "Display title. The page title for `scraped_link`, the artifact name for `artifact`, and the task title for `task` types. `null` on other types." 396 type: Required[str] 397 'The attachment type. One of `"file"`, `"scraped_link"`, `"artifact"`, `"task"`, `"media"`, or `"action"`. Determines which additional fields are present.' 398 url: str | None 399 "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." 400 variants: list[MessageUpdatedPayloadMessageAttachmentsItemVariantsItem] | None 401 "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only. `null` otherwise." 402 version: int | None 403 "Version number of the attached artifact at the time of attachment. Present on `artifact` type only. `null` otherwise." 404 width: int | None 405 "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.
The media category, e.g. "video" or "audio". Present on media type only. null otherwise.
The full embedded object payload. For task type, contains the task record. For action type, contains the action definition. null 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", or "action". 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. null otherwise.
408class MessageUpdatedPayloadMessageReactionsItem(TypedDict, total=False): 409 payload: dict[str, Any] | None 410 'Type-specific reaction data. For `"emoji_reaction"` reactions, contains an `emoji` key with the Unicode emoji string (e.g., `" "`).' 411 type: Required[str] 412 'Reaction type identifier. Currently always `"emoji_reaction"` for emoji-based reactions.' 413 user: str | None 414 "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., " ").
417class MessageUpdatedPayloadMessage(TypedDict, total=False): 418 actors: list[MessageUpdatedPayloadMessageActorsItem] | None 419 "Resolved actor descriptors for the message sender, combining identity and display metadata. Always contains exactly one entry." 420 agent: str | None 421 "ID of the agent user that sent this message (`agi_...`). `null` for messages sent by human users." 422 agent_mode: Literal["cli", "embedded"] | None 423 "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." 424 attachments: list[MessageUpdatedPayloadMessageAttachmentsItem] | None 425 "Files, links, tasks, media, artifacts, and actions attached to this message. Empty array if there are no attachments." 426 branched_thread: str | None 427 "ID of the thread that was branched from this message (`thr_...`). `null` if this message has not spawned a branch thread." 428 content: str | None 429 "Text content of the message. `null` for messages that contain only attachments." 430 created_at: datetime | None 431 "When the message was posted (ISO 8601)." 432 has_replies: bool | None 433 "Whether this message has at least one reply. Only present when explicitly requested or computed by the server." 434 id: Required[str] 435 "Message ID (`msg_...`)." 436 idempotency_key: str | None 437 "Client-supplied idempotency key used to deduplicate message sends. `null` if the sender did not provide one." 438 legacy_agent: str | None 439 "Identifier of the legacy chat agent that sent this message, if applicable. `null` for messages sent by users or modern agent users." 440 metadata: dict[str, Any] | None 441 "Arbitrary key-value metadata attached to the message. Always present; defaults to an empty object when no metadata has been set." 442 org: str | None 443 "ID of the organization that owns this message (`org_...`)." 444 reactions: list[MessageUpdatedPayloadMessageReactionsItem] | None 445 "Emoji and other reactions added to this message by users. Empty array if no reactions have been added or the association is not preloaded." 446 rendering_mode: str | None 447 '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.' 448 replies: list[dict[str, Any]] | None 449 "Inline array of reply messages, each serialized as a full message object. Only present when the server has preloaded replies for this message." 450 replies_after_cursor: str | None 451 "Opaque pagination cursor to fetch replies posted after the current page. Only present when inline replies are included in the response." 452 replies_before_cursor: str | None 453 "Opaque pagination cursor to fetch replies posted before the current page. Only present when inline replies are included in the response." 454 reply_count: int | None 455 "Total number of direct replies to this message. Only present when explicitly requested or computed by the server." 456 reply_to: dict[str, Any] | None 457 "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." 458 sandbox: str | None 459 "ID of the developer sandbox this message belongs to (`dsb_...`). `null` for non-sandbox messages." 460 team: str | None 461 "ID of the team this message is scoped to (`tem_...`). `null` if the message is not team-scoped." 462 thread: str | None 463 "ID of the thread this message belongs to (`thr_...`). `null` for messages not yet associated with a thread." 464 user: str | None 465 "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."
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.
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 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.
468class MessageUpdatedPayload(TypedDict, total=False): 469 "Broadcast when a message is updated or removed" 470 471 message: MessageUpdatedPayloadMessage | None 472 "A chat message posted in a thread, including its content, author, attachments, reactions, and optional reply metadata." 473 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.
476class ThreadEventPayload(TypedDict, total=False): 477 "Broadcast thread-level events (agent updates, read state, unread counts)" 478 479 payload: dict[str, Any] | None 480 thread_id: str | None 481 type: str | None
Broadcast thread-level events (agent updates, read state, unread counts)
484class SystemEventPayload(TypedDict, total=False): 485 "Broadcast system-wide events" 486 487 event: dict[str, Any] | None
Broadcast system-wide events
490class TypingPayloadActorProfilePicture(TypedDict, total=False): 491 file: str | None 492 "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file." 493 height: int | None 494 "Height of the image in pixels. `null` if not known." 495 media: str | None 496 "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity." 497 mime_type: str | None 498 'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.' 499 refresh_url: str | None 500 "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." 501 url: str | None 502 "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires." 503 width: int | None 504 "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.
507class TypingPayloadActor(TypedDict, total=False): 508 alias: str | None 509 "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured." 510 id: str | None 511 'Composite actor identifier. Format is `"user-<usr_...>"` for human users or `"agent-<agi_...>"` for agents.' 512 name: str | None 513 "Display name of the actor shown in the UI. `null` if no name is set." 514 profile_picture: TypingPayloadActorProfilePicture | None 515 "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.
518class TypingPayload(TypedDict, total=False): 519 "Broadcast when a participant (human or agent) starts or stops typing. Ephemeral; never persisted." 520 521 actor: TypingPayloadActor | None 522 "The entity that authored a message, either a human user or an agent." 523 is_typing: bool | None 524 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.
530class ApiChatChannel: 531 def __init__(self, channel, join_response=None): 532 self._channel = channel 533 self.join_response = join_response 534 535 # Join a team-scoped thread by ID 536 @staticmethod 537 def topic_team_thread(team_id: str, thread_id: str) -> str: 538 return f"api:chat:team:{team_id}:thread:{thread_id}" 539 540 # Join a team-scoped thread by ID 541 @classmethod 542 async def join_team_thread( 543 cls, 544 socket: "Socket", 545 team_id: str, 546 thread_id: str, 547 *, 548 after_cursor: str | None = None, 549 before_cursor: str | None = None, 550 include_metadata: bool | None = None, 551 limit: int | None = None, 552 ) -> "ApiChatChannel": 553 topic = cls.topic_team_thread(team_id, thread_id) 554 channel = socket.channel(topic) 555 payload: dict[str, object] = {} 556 if after_cursor is not None: 557 payload["after_cursor"] = after_cursor 558 if before_cursor is not None: 559 payload["before_cursor"] = before_cursor 560 if include_metadata is not None: 561 payload["include_metadata"] = include_metadata 562 if limit is not None: 563 payload["limit"] = limit 564 join_response = await channel.join(payload) 565 return cls(channel, join_response) 566 567 # Join or create a team-scoped keyed thread 568 @staticmethod 569 def topic_team_keyed(team_id: str, key: str) -> str: 570 return f"api:chat:team:{team_id}:key:{key}" 571 572 # Join or create a team-scoped keyed thread 573 @classmethod 574 async def join_team_keyed( 575 cls, 576 socket: "Socket", 577 team_id: str, 578 key: str, 579 *, 580 after_cursor: str | None = None, 581 before_cursor: str | None = None, 582 include_metadata: bool | None = None, 583 limit: int | None = None, 584 ) -> "ApiChatChannel": 585 topic = cls.topic_team_keyed(team_id, key) 586 channel = socket.channel(topic) 587 payload: dict[str, object] = {} 588 if after_cursor is not None: 589 payload["after_cursor"] = after_cursor 590 if before_cursor is not None: 591 payload["before_cursor"] = before_cursor 592 if include_metadata is not None: 593 payload["include_metadata"] = include_metadata 594 if limit is not None: 595 payload["limit"] = limit 596 join_response = await channel.join(payload) 597 return cls(channel, join_response) 598 599 # Join a team-scoped transient (ephemeral) thread 600 @staticmethod 601 def topic_team_transient(team_id: str, key: str) -> str: 602 return f"api:chat:team:{team_id}:transient:{key}" 603 604 # Join a team-scoped transient (ephemeral) thread 605 @classmethod 606 async def join_team_transient( 607 cls, 608 socket: "Socket", 609 team_id: str, 610 key: str, 611 *, 612 after_cursor: str | None = None, 613 before_cursor: str | None = None, 614 include_metadata: bool | None = None, 615 limit: int | None = None, 616 ) -> "ApiChatChannel": 617 topic = cls.topic_team_transient(team_id, key) 618 channel = socket.channel(topic) 619 payload: dict[str, object] = {} 620 if after_cursor is not None: 621 payload["after_cursor"] = after_cursor 622 if before_cursor is not None: 623 payload["before_cursor"] = before_cursor 624 if include_metadata is not None: 625 payload["include_metadata"] = include_metadata 626 if limit is not None: 627 payload["limit"] = limit 628 join_response = await channel.join(payload) 629 return cls(channel, join_response) 630 631 # Join a user-scoped thread by ID 632 @staticmethod 633 def topic_user_thread(thread_id: str) -> str: 634 return f"api:chat:user:thread:{thread_id}" 635 636 # Join a user-scoped thread by ID 637 @classmethod 638 async def join_user_thread( 639 cls, 640 socket: "Socket", 641 thread_id: str, 642 *, 643 after_cursor: str | None = None, 644 before_cursor: str | None = None, 645 include_metadata: bool | None = None, 646 limit: int | None = None, 647 ) -> "ApiChatChannel": 648 topic = cls.topic_user_thread(thread_id) 649 channel = socket.channel(topic) 650 payload: dict[str, object] = {} 651 if after_cursor is not None: 652 payload["after_cursor"] = after_cursor 653 if before_cursor is not None: 654 payload["before_cursor"] = before_cursor 655 if include_metadata is not None: 656 payload["include_metadata"] = include_metadata 657 if limit is not None: 658 payload["limit"] = limit 659 join_response = await channel.join(payload) 660 return cls(channel, join_response) 661 662 # Join or create a user-scoped keyed thread 663 @staticmethod 664 def topic_user_keyed(key: str) -> str: 665 return f"api:chat:user:key:{key}" 666 667 # Join or create a user-scoped keyed thread 668 @classmethod 669 async def join_user_keyed( 670 cls, 671 socket: "Socket", 672 key: str, 673 *, 674 after_cursor: str | None = None, 675 before_cursor: str | None = None, 676 include_metadata: bool | None = None, 677 limit: int | None = None, 678 ) -> "ApiChatChannel": 679 topic = cls.topic_user_keyed(key) 680 channel = socket.channel(topic) 681 payload: dict[str, object] = {} 682 if after_cursor is not None: 683 payload["after_cursor"] = after_cursor 684 if before_cursor is not None: 685 payload["before_cursor"] = before_cursor 686 if include_metadata is not None: 687 payload["include_metadata"] = include_metadata 688 if limit is not None: 689 payload["limit"] = limit 690 join_response = await channel.join(payload) 691 return cls(channel, join_response) 692 693 # Join a user-scoped transient (ephemeral) thread 694 @staticmethod 695 def topic_user_transient(key: str) -> str: 696 return f"api:chat:user:transient:{key}" 697 698 # Join a user-scoped transient (ephemeral) thread 699 @classmethod 700 async def join_user_transient( 701 cls, 702 socket: "Socket", 703 key: str, 704 *, 705 after_cursor: str | None = None, 706 before_cursor: str | None = None, 707 include_metadata: bool | None = None, 708 limit: int | None = None, 709 ) -> "ApiChatChannel": 710 topic = cls.topic_user_transient(key) 711 channel = socket.channel(topic) 712 payload: dict[str, object] = {} 713 if after_cursor is not None: 714 payload["after_cursor"] = after_cursor 715 if before_cursor is not None: 716 payload["before_cursor"] = before_cursor 717 if include_metadata is not None: 718 payload["include_metadata"] = include_metadata 719 if limit is not None: 720 payload["limit"] = limit 721 join_response = await channel.join(payload) 722 return cls(channel, join_response) 723 724 # Leave the underlying channel. 725 async def leave(self): 726 await self._channel.leave() 727 728 # Fork a sub-thread from an existing message 729 async def api_chat_fork_thread(self, payload: ApiChatForkThreadInput) -> dict[str, Any]: 730 return await self._channel.push("api:chat:fork_thread", payload) 731 732 # Mark a thread as read up to a given message 733 async def api_chat_mark_thread_read( 734 self, payload: ApiChatMarkThreadReadInput 735 ) -> dict[str, Any]: 736 return await self._channel.push("api:chat:mark_thread_read", payload) 737 738 # List all messages in the current thread 739 async def api_chat_list_messages(self, payload: dict) -> dict[str, Any]: 740 return await self._channel.push("api:chat:list_messages", payload) 741 742 # Load additional messages with cursor-based pagination 743 async def api_chat_load_more_messages( 744 self, payload: ApiChatLoadMoreMessagesInput 745 ) -> dict[str, Any]: 746 return await self._channel.push("api:chat:load_more_messages", payload) 747 748 # Post a new message with optional uploads and reply-to 749 async def api_chat_post_message(self, payload: ApiChatPostMessageInput) -> dict[str, Any]: 750 return await self._channel.push("api:chat:post_message", payload) 751 752 # Post a simple text message 753 async def api_chat_post_simple_message( 754 self, payload: ApiChatPostSimpleMessageInput 755 ) -> dict[str, Any]: 756 return await self._channel.push("api:chat:post_simple_message", payload) 757 758 # Edit an existing message's content 759 async def api_chat_edit_message(self, payload: ApiChatEditMessageInput) -> dict[str, Any]: 760 return await self._channel.push("api:chat:edit_message", payload) 761 762 # Delete a message 763 async def api_chat_delete_message(self, payload: ApiChatDeleteMessageInput) -> dict[str, Any]: 764 return await self._channel.push("api:chat:delete_message", payload) 765 766 # Add an emoji reaction to a message 767 async def api_chat_add_reaction(self, payload: ApiChatAddReactionInput) -> dict[str, Any]: 768 return await self._channel.push("api:chat:add_reaction", payload) 769 770 # Remove an emoji reaction from a message 771 async def api_chat_remove_reaction(self, payload: ApiChatRemoveReactionInput) -> dict[str, Any]: 772 return await self._channel.push("api:chat:remove_reaction", payload) 773 774 # Signal that the current user has started or stopped typing in the thread 775 async def api_chat_typing(self, payload: ApiChatTypingInput) -> dict[str, Any]: 776 return await self._channel.push("api:chat:typing", payload) 777 778 # Broadcast when a new message is added to a thread 779 def on_message_added( 780 self, callback: Callable[[MessageAddedPayload], None] 781 ) -> Callable[[], None]: 782 return self._channel.on("message_added", callback) 783 784 # Broadcast when a message is updated or removed 785 def on_message_updated( 786 self, callback: Callable[[MessageUpdatedPayload], None] 787 ) -> Callable[[], None]: 788 return self._channel.on("message_updated", callback) 789 790 # Broadcast thread-level events (agent updates, read state, unread counts) 791 def on_thread_event(self, callback: Callable[[ThreadEventPayload], None]) -> Callable[[], None]: 792 return self._channel.on("thread_event", callback) 793 794 # Broadcast system-wide events 795 def on_system_event(self, callback: Callable[[SystemEventPayload], None]) -> Callable[[], None]: 796 return self._channel.on("system_event", callback) 797 798 # Broadcast when a participant (human or agent) starts or stops typing. Ephemeral; never persisted. 799 def on_typing(self, callback: Callable[[TypingPayload], None]) -> Callable[[], None]: 800 return self._channel.on("typing", callback)
541 @classmethod 542 async def join_team_thread( 543 cls, 544 socket: "Socket", 545 team_id: str, 546 thread_id: str, 547 *, 548 after_cursor: str | None = None, 549 before_cursor: str | None = None, 550 include_metadata: bool | None = None, 551 limit: int | None = None, 552 ) -> "ApiChatChannel": 553 topic = cls.topic_team_thread(team_id, thread_id) 554 channel = socket.channel(topic) 555 payload: dict[str, object] = {} 556 if after_cursor is not None: 557 payload["after_cursor"] = after_cursor 558 if before_cursor is not None: 559 payload["before_cursor"] = before_cursor 560 if include_metadata is not None: 561 payload["include_metadata"] = include_metadata 562 if limit is not None: 563 payload["limit"] = limit 564 join_response = await channel.join(payload) 565 return cls(channel, join_response)
573 @classmethod 574 async def join_team_keyed( 575 cls, 576 socket: "Socket", 577 team_id: str, 578 key: str, 579 *, 580 after_cursor: str | None = None, 581 before_cursor: str | None = None, 582 include_metadata: bool | None = None, 583 limit: int | None = None, 584 ) -> "ApiChatChannel": 585 topic = cls.topic_team_keyed(team_id, key) 586 channel = socket.channel(topic) 587 payload: dict[str, object] = {} 588 if after_cursor is not None: 589 payload["after_cursor"] = after_cursor 590 if before_cursor is not None: 591 payload["before_cursor"] = before_cursor 592 if include_metadata is not None: 593 payload["include_metadata"] = include_metadata 594 if limit is not None: 595 payload["limit"] = limit 596 join_response = await channel.join(payload) 597 return cls(channel, join_response)
605 @classmethod 606 async def join_team_transient( 607 cls, 608 socket: "Socket", 609 team_id: str, 610 key: str, 611 *, 612 after_cursor: str | None = None, 613 before_cursor: str | None = None, 614 include_metadata: bool | None = None, 615 limit: int | None = None, 616 ) -> "ApiChatChannel": 617 topic = cls.topic_team_transient(team_id, key) 618 channel = socket.channel(topic) 619 payload: dict[str, object] = {} 620 if after_cursor is not None: 621 payload["after_cursor"] = after_cursor 622 if before_cursor is not None: 623 payload["before_cursor"] = before_cursor 624 if include_metadata is not None: 625 payload["include_metadata"] = include_metadata 626 if limit is not None: 627 payload["limit"] = limit 628 join_response = await channel.join(payload) 629 return cls(channel, join_response)
637 @classmethod 638 async def join_user_thread( 639 cls, 640 socket: "Socket", 641 thread_id: str, 642 *, 643 after_cursor: str | None = None, 644 before_cursor: str | None = None, 645 include_metadata: bool | None = None, 646 limit: int | None = None, 647 ) -> "ApiChatChannel": 648 topic = cls.topic_user_thread(thread_id) 649 channel = socket.channel(topic) 650 payload: dict[str, object] = {} 651 if after_cursor is not None: 652 payload["after_cursor"] = after_cursor 653 if before_cursor is not None: 654 payload["before_cursor"] = before_cursor 655 if include_metadata is not None: 656 payload["include_metadata"] = include_metadata 657 if limit is not None: 658 payload["limit"] = limit 659 join_response = await channel.join(payload) 660 return cls(channel, join_response)
668 @classmethod 669 async def join_user_keyed( 670 cls, 671 socket: "Socket", 672 key: str, 673 *, 674 after_cursor: str | None = None, 675 before_cursor: str | None = None, 676 include_metadata: bool | None = None, 677 limit: int | None = None, 678 ) -> "ApiChatChannel": 679 topic = cls.topic_user_keyed(key) 680 channel = socket.channel(topic) 681 payload: dict[str, object] = {} 682 if after_cursor is not None: 683 payload["after_cursor"] = after_cursor 684 if before_cursor is not None: 685 payload["before_cursor"] = before_cursor 686 if include_metadata is not None: 687 payload["include_metadata"] = include_metadata 688 if limit is not None: 689 payload["limit"] = limit 690 join_response = await channel.join(payload) 691 return cls(channel, join_response)
699 @classmethod 700 async def join_user_transient( 701 cls, 702 socket: "Socket", 703 key: str, 704 *, 705 after_cursor: str | None = None, 706 before_cursor: str | None = None, 707 include_metadata: bool | None = None, 708 limit: int | None = None, 709 ) -> "ApiChatChannel": 710 topic = cls.topic_user_transient(key) 711 channel = socket.channel(topic) 712 payload: dict[str, object] = {} 713 if after_cursor is not None: 714 payload["after_cursor"] = after_cursor 715 if before_cursor is not None: 716 payload["before_cursor"] = before_cursor 717 if include_metadata is not None: 718 payload["include_metadata"] = include_metadata 719 if limit is not None: 720 payload["limit"] = limit 721 join_response = await channel.join(payload) 722 return cls(channel, join_response)