22
33Based on the Linear API and webhook format.
44See: https://linear.app/developers
5+
6+ Agent-session types (``mode``, the ``kind`` discriminator, the agent-session
7+ raw-message variant, and ``AgentSessionEventWebhookPayload``) are a faithful
8+ port of upstream ``packages/adapter-linear/src/types.ts`` (vercel/chat,
9+ adapter-linear 4.27.0 / chat@4.31.0). Upstream re-uses ``@linear/sdk`` and
10+ ``@linear/sdk/webhooks`` types; our adapter is raw GraphQL, so the
11+ agent-session webhook payload is hand-authored to mirror the SDK shape that
12+ upstream ``index.ts`` consumes.
513"""
614
715from __future__ import annotations
816
917from dataclasses import dataclass
10- from typing import Any , TypedDict
18+ from typing import Any , Literal , TypedDict
1119
1220from chat_sdk .logger import Logger
1321
1422# =============================================================================
1523# Configuration
1624# =============================================================================
1725
26+ # Incoming webhook handling mode for the Linear adapter. Mirrors upstream
27+ # ``LinearAdapterMode`` (types.ts:44). ``"comments"`` is the data-change
28+ # webhook model (the existing behavior); ``"agent-sessions"`` is the app-actor
29+ # model used for ``AgentSessionEvent`` webhooks.
30+ LinearAdapterMode = Literal ["agent-sessions" , "comments" ]
31+
1832
1933@dataclass
2034class LinearAdapterBaseConfig :
@@ -28,6 +42,11 @@ class LinearAdapterBaseConfig:
2842 api_url : str | None = None
2943 # Logger instance for error reporting. Defaults to ConsoleLogger.
3044 logger : Logger | None = None
45+ # Controls which inbound Linear webhook model should trigger message
46+ # handling. Defaults to "comments". Use "agent-sessions" for app-actor
47+ # installs. Faithful port of upstream ``config.mode ?? "comments"``
48+ # (types.ts:67, index.ts:236).
49+ mode : LinearAdapterMode | None = None
3150 # Bot display name for @-mention detection.
3251 # Defaults to LINEAR_BOT_USERNAME env var or "linear-bot".
3352 user_name : str | None = None
@@ -130,6 +149,23 @@ class LinearThreadId:
130149 issue_id : str
131150 # Root comment ID for comment-level threads (optional)
132151 comment_id : str | None = None
152+ # Agent session UUID for app-actor interactions (optional). Faithful port of
153+ # upstream ``LinearThreadId.agentSessionId`` (types.ts:189).
154+ agent_session_id : str | None = None
155+
156+
157+ @dataclass (frozen = True )
158+ class LinearAgentSessionThreadId (LinearThreadId ):
159+ """Decoded thread ID for Linear threads associated with agent sessions.
160+
161+ Faithful port of upstream ``LinearAgentSessionThreadId``
162+ (``LinearThreadId & { agentSessionId: string }``, types.ts:203). Narrows
163+ ``agent_session_id`` to a required, non-optional ``str`` so that downstream
164+ agent-session code (L4) can rely on the field being present.
165+ """
166+
167+ # Required for agent-session threads (narrows the optional base field).
168+ agent_session_id : str = ""
133169
134170
135171# =============================================================================
@@ -222,19 +258,189 @@ class ReactionWebhookPayload(TypedDict, total=False):
222258 webhookTimestamp : int
223259
224260
261+ # -----------------------------------------------------------------------------
262+ # Agent-session webhook payload (mode="agent-sessions")
263+ # -----------------------------------------------------------------------------
264+ #
265+ # Hand-authored to mirror upstream ``@linear/sdk/webhooks``
266+ # ``AgentSessionEventWebhookPayload`` as consumed by upstream
267+ # ``adapter-linear/src/index.ts``. Webhook payloads are external JSON, so the
268+ # RAW Linear wire keys (camelCase) are kept verbatim — no snake_case aliasing.
269+ # These are NetworkError-prone external inputs; ``total=False`` mirrors the
270+ # optional-heavy SDK shape and lets the parse logic (L3) read fields defensively.
271+
272+
273+ class AgentSessionCommentChild (TypedDict , total = False ):
274+ """Root comment of the thread an agent session is attached to.
275+
276+ Mirrors ``CommentChildWebhookPayload`` as consumed for
277+ ``agentSession.comment`` (index.ts:1026-1035).
278+ """
279+
280+ id : str
281+ body : str
282+ userId : str
283+
284+
285+ class AgentSessionIssueChild (TypedDict , total = False ):
286+ """Issue an agent session is associated with.
287+
288+ Mirrors ``IssueWithDescriptionChildWebhookPayload`` as consumed for
289+ ``agentSession.issue`` (index.ts:959).
290+ """
291+
292+ id : str
293+
294+
295+ class AgentSessionUserChild (TypedDict , total = False ):
296+ """Human user responsible for the agent session.
297+
298+ Mirrors ``UserChildWebhookPayload`` as consumed for
299+ ``agentSession.creator`` (index.ts:1037-1044).
300+ """
301+
302+ id : str
303+ name : str
304+ email : str
305+ avatarUrl : str
306+ url : str
307+
308+
309+ class AgentSessionWebhookPayload (TypedDict , total = False ):
310+ """The agent session an ``AgentSessionEvent`` belongs to.
311+
312+ Mirrors ``AgentSessionWebhookPayload`` (@linear/sdk/webhooks) as consumed by
313+ upstream ``index.ts``.
314+ """
315+
316+ id : str
317+ appUserId : str
318+ issueId : str
319+ issue : AgentSessionIssueChild
320+ commentId : str
321+ sourceCommentId : str
322+ comment : AgentSessionCommentChild
323+ creator : AgentSessionUserChild
324+ url : str
325+ status : str
326+ summary : str
327+ sourceMetadata : dict [str , Any ]
328+
329+
330+ class AgentActivityWebhookContent (TypedDict , total = False ):
331+ """Content of an agent activity (e.g. the prompt body).
332+
333+ Mirrors the ``content`` discriminated object consumed at index.ts:984.
334+ """
335+
336+ type : str # e.g. "prompt"
337+ body : str
338+
339+
340+ class AgentActivityWebhookPayload (TypedDict , total = False ):
341+ """The agent activity that triggered a ``prompted`` event.
342+
343+ Mirrors ``AgentActivityWebhookPayload`` (@linear/sdk/webhooks) as consumed
344+ by upstream ``index.ts`` (e.g. index.ts:968-998).
345+ """
346+
347+ id : str
348+ sourceCommentId : str
349+ content : AgentActivityWebhookContent
350+ user : AgentSessionUserChild
351+ createdAt : str
352+
353+
354+ class GuidanceRuleWebhookPayload (TypedDict , total = False ):
355+ """A single guidance rule for the agent's behavior."""
356+
357+ body : str
358+
359+
360+ class CommentChildWebhookPayload (TypedDict , total = False ):
361+ """A comment in the thread before the agent session was initiated."""
362+
363+ id : str
364+ body : str
365+
366+
367+ class AgentSessionEventWebhookPayload (TypedDict , total = False ):
368+ """Webhook payload for ``AgentSessionEvent`` events (mode="agent-sessions").
369+
370+ Faithful port of upstream ``AgentSessionEventWebhookPayload``
371+ (@linear/sdk/webhooks), hand-authored to match the shape consumed by
372+ upstream ``adapter-linear/src/index.ts``. Raw Linear wire keys (camelCase).
373+ """
374+
375+ type : str # "AgentSessionEvent"
376+ action : str # "created" | "prompted"
377+ createdAt : str
378+ appUserId : str
379+ oauthClientId : str
380+ organizationId : str
381+ webhookId : str
382+ webhookTimestamp : int
383+ # Formatted prompt with relevant context; present only for "created" events.
384+ promptContext : str
385+ agentSession : AgentSessionWebhookPayload
386+ agentActivity : AgentActivityWebhookPayload
387+ guidance : list [GuidanceRuleWebhookPayload ]
388+ previousComments : list [CommentChildWebhookPayload ]
389+
390+
225391# Union type for all webhook payloads
226- LinearWebhookPayload = CommentWebhookPayload | ReactionWebhookPayload
392+ LinearWebhookPayload = CommentWebhookPayload | ReactionWebhookPayload | AgentSessionEventWebhookPayload
227393
228394
229395# =============================================================================
230396# Raw Message Type
231397# =============================================================================
398+ #
399+ # Discriminated union on the ``kind`` field. Faithful port of upstream
400+ # ``LinearRawMessage`` (types.ts:279). Every variant MUST carry ``kind`` so the
401+ # union discriminates cleanly; constructing a raw message without ``kind``
402+ # breaks the union (emit/parse symmetry). The existing (comment-based)
403+ # producers in ``adapter.py`` all set ``kind="comment"``.
404+
405+
406+ class LinearCommentRawMessage (TypedDict , total = False ):
407+ """Platform-specific raw message for a standard Linear comment.
408+
409+ Faithful port of upstream ``LinearCommentRawMessage`` (types.ts:258).
410+ ``kind`` and ``comment`` are required; ``organizationId`` is part of the
411+ upstream base but our raw-GraphQL producers do not always have it on hand,
412+ so it stays optional (``total=False``) for back-compat with the existing
413+ comment path.
414+ """
415+
416+ # Raw message kind discriminator. Always "comment" for this variant.
417+ kind : Literal ["comment" ]
418+ # The raw comment data from webhook or API.
419+ comment : LinearCommentData
420+ # Organization ID from the webhook or request context.
421+ organizationId : str
422+
232423
424+ class LinearAgentSessionCommentRawMessage (TypedDict , total = False ):
425+ """Platform-specific raw message for a comment backed by an agent session.
233426
234- class LinearRawMessage (TypedDict , total = False ):
235- """Platform-specific raw message type for Linear."""
427+ Faithful port of upstream ``LinearAgentSessionCommentRawMessage``
428+ (types.ts:265). Carries the agent session the comment belongs to plus an
429+ optional prompt context.
430+ """
236431
237- # The raw comment data from webhook or API
432+ # Raw message kind discriminator. Always "agent_session_comment".
433+ kind : Literal ["agent_session_comment" ]
434+ # The visible Linear comment backing this message.
238435 comment : LinearCommentData
239- # Organization ID from the webhook
436+ # The agent session the comment belongs to.
437+ agentSessionId : str
438+ # The prompt context associated with this agent session comment (optional).
439+ agentSessionPromptContext : str
440+ # Organization ID from the webhook or request context.
240441 organizationId : str
442+
443+
444+ # Platform-specific raw message type for Linear (discriminated union on `kind`).
445+ # Faithful port of upstream ``LinearRawMessage`` (types.ts:279).
446+ LinearRawMessage = LinearCommentRawMessage | LinearAgentSessionCommentRawMessage
0 commit comments