import { MastraWorker } from '@mastra/core/worker'; import type { WorkerDeps } from '@mastra/core/worker'; import type { IntegrationStorageHandle } from '../../../storage/domains/integrations/base.js'; import type { FactoryProjectsStorage } from '../../../storage/domains/projects/base.js'; import type { WorkItemsStorage } from '../../../storage/domains/work-items/base.js'; import type { IssueReconciler } from '../../issue-reconciler.js'; import type { LinearRulesIngress } from '../../linear/rules.js'; import { PlatformApiClient } from '../api-client.js'; type EventCursor = { afterEventId: string; } | { afterTimestamp: number; }; type PlatformLinearEventWorkerSettings = { version: 1; workspaces: Record; }; export type PlatformLinearEventStorage = IntegrationStorageHandle, PlatformLinearEventWorkerSettings, Record>; /** * Minimal envelope shape mirrored from `@platform/linear`. Kept opaque * (`data: unknown`) — every consumer demuxes by `type`. */ export interface LinearWebhookEnvelope { type: string; action: string | null; createdAt: string | null; webhookTimestamp: number | null; linearOrganizationId: string | null; oauthClientId: string | null; url: string | null; data: unknown; } export interface LinearEventLogEntry { id: string; timestamp: number; envelope: LinearWebhookEnvelope; } export interface PlatformLinearWorkspace { linearWorkspaceId: string; linearWorkspaceName?: string; } /** * Everything the worker needs to drive ingest + reconciliation without * pulling in the full `PlatformLinearIntegration` type (circular). */ export interface PlatformLinearEventDispatchIntegration { listWorkspaces(): Promise; } export interface PlatformLinearEventWorkerConfig { client: PlatformApiClient; linear: PlatformLinearEventDispatchIntegration; storage: PlatformLinearEventStorage; projects: Pick; /** * Used to scope event dispatch to `(orgId, factoryProjectId)` pairs that * already have a work item linked to the incoming Linear issue. Without * this scoping, a workspace-scoped event would fan out to every project in * every org and materialize a triage card in each — a cross-tenant leak. * First-observation of a Linear issue happens through the user-authenticated * `/web/linear/issues?factoryProjectId=...` intake path instead. */ workItems: Pick; /** Called with a single-issue rules ingress derived from an `Issue` webhook. */ ingestFactoryIssue?: (input: LinearRulesIngress) => Promise; reconcileFactoryState?: IssueReconciler; /** When false the worker skips event tailing and only runs the reconcile sweep. */ pollEventsEnabled?: boolean; intervalMs?: number; reconcileIntervalMs?: number; now?: () => number; } /** * Tails the Platform Linear event stream (Redis-backed, per workspace) and * runs the same folded issue-reconcile sweep pattern as * `PlatformGithubEventWorker`. Issue events are translated into a normal * `LinearRulesIngress` so downstream rule dispatch is identical to the * polling path. * * Cursor state is persisted per workspace via the integration's generic * settings surface, keyed by a well-known worker identity. Cold start uses * `afterTimestamp: now - 1` to avoid replaying the 14-day stream backlog. * * ## Delivery semantics * * **At-most-once** per event. The cursor advances to the last event ID in a * page after all dispatch attempts on that page complete, regardless of * whether individual ingest calls threw. Ingest failures are logged, the * offending event is not retried, and drift is caught by the folded * `LinearIssueReconciler` sweep on its own cadence (default 5 minutes). This * matches `PlatformGithubEventWorker` and avoids poison-pill events blocking * the whole stream. Any consumer that needs exactly-once must idempotently * handle the same issue arriving via both the event path and the reconcile * path. */ export declare class PlatformLinearEventWorker extends MastraWorker { #private; readonly name = "platform-linear-events"; constructor(config: PlatformLinearEventWorkerConfig); init(deps: WorkerDeps): Promise; start(): Promise; stop(): Promise; get isRunning(): boolean; } export {}; //# sourceMappingURL=event-worker.d.ts.map