From 3d2483a4ded8ba18bdfd0fc1ddf416a98ad0050e Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Sat, 25 Apr 2026 21:20:00 +0100 Subject: [PATCH] refactor(sdk): extract browser-safe chat types into ai-shared Pulls PENDING_MESSAGE_INJECTED_TYPE, ChatTaskWirePayload, and the client-data inference helpers out of ai.ts (~7000 lines, statically imports node:* via the skills runtime) into a new ai-shared.ts that stays free of node-only imports. chat.ts and chat-react.ts now reach for these via ai-shared so browser bundlers don't trace ai.ts's entire module graph (Turbopack rejected the node: builtins outright). --- packages/trigger-sdk/src/v3/ai-shared.ts | 124 +++++++++++++++++++++ packages/trigger-sdk/src/v3/chat-client.ts | 2 +- packages/trigger-sdk/src/v3/chat-react.ts | 2 +- 3 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 packages/trigger-sdk/src/v3/ai-shared.ts diff --git a/packages/trigger-sdk/src/v3/ai-shared.ts b/packages/trigger-sdk/src/v3/ai-shared.ts new file mode 100644 index 000000000..884c11fe2 --- /dev/null +++ b/packages/trigger-sdk/src/v3/ai-shared.ts @@ -0,0 +1,124 @@ +/** + * Browser-safe primitives shared between `@trigger.dev/sdk/ai` (server) and + * `@trigger.dev/sdk/chat` / `@trigger.dev/sdk/chat/react` (client). + * + * This module exists to keep `ai.ts` reachable only from the server graph. + * `ai.ts` weighs in at ~7000 lines and statically imports the agent-skills + * runtime (which uses `node:child_process` / `node:fs/promises`). When a + * browser bundle imports a runtime value from `ai.ts` — historically the + * `PENDING_MESSAGE_INJECTED_TYPE` constant in `chat-react.ts` — the bundler + * traces `ai.ts`'s entire module graph into the client chunk and hits the + * `node:` builtins, which Turbopack rejects outright (and webpack flags as + * a "Critical dependency" warning). + * + * Anything in this file MUST stay free of `node:*` imports and free of any + * import from `ai.ts`. + */ + +import type { Task, AnyTask } from "@trigger.dev/core/v3"; +import type { UIMessage } from "ai"; + +/** + * Message-part `type` value for the pending-message data part the agent + * injects when a follow-up message arrives mid-turn. + */ +export const PENDING_MESSAGE_INJECTED_TYPE = "data-pending-message-injected" as const; + +/** + * The wire payload shape sent by `TriggerChatTransport`. + * Uses `metadata` to match the AI SDK's `ChatRequestOptions` field name. + */ +export type ChatTaskWirePayload = { + messages: TMessage[]; + chatId: string; + trigger: "submit-message" | "regenerate-message" | "preload" | "close" | "action"; + messageId?: string; + metadata?: TMetadata; + /** Custom action payload when `trigger` is `"action"`. Validated against `actionSchema` on the backend. */ + action?: unknown; + /** Whether this run is continuing an existing chat whose previous run ended. */ + continuation?: boolean; + /** The run ID of the previous run (only set when `continuation` is true). */ + previousRunId?: string; + /** Override idle timeout for this run (seconds). Set by transport.preload(). */ + idleTimeoutInSeconds?: number; + /** + * The friendlyId of the Session primitive backing this chat. The + * transport opens (or lazy-creates) the session with + * `externalId = chatId` on first message, then sends this friendlyId + * through to the run so the agent can attach to `.in` / `.out` + * without needing to round-trip through the control plane again. + * Optional for backward-compat while the migration is in flight; + * required once the legacy run-scoped stream path is removed. + */ + sessionId?: string; + /** + * Client-side `chat.store` value sent by the transport. Applied at turn + * start before `run()` fires, overwriting any in-memory store value on the + * agent (last-write-wins). + * + * The transport queues this via `setStore` / `applyStorePatch` and flushes + * it with the next `sendMessage`. On the agent you typically don't read + * this directly — it's applied into `chat.store` transparently. + */ + incomingStore?: unknown; +}; + +/** + * One chunk on the chat input stream. `kind` discriminates the variants — + * a single ordered stream now carries all the signals the old three-stream + * split did (`chat-messages`, `chat-stop`, plus action messages piggybacked + * on `chat-messages`). + */ +export type ChatInputChunk = + | { + kind: "message"; + /** + * Full wire payload for a new user message or regeneration. Mirrors + * what the legacy `chat-messages` input stream carried. + */ + payload: ChatTaskWirePayload; + } + | { + kind: "stop"; + /** Optional human-readable reason. Maps to the legacy `chat-stop` record. */ + message?: string; + }; + +/** + * Extracts the client-data (`metadata`) type from a chat task. + * + * @example + * ```ts + * import type { InferChatClientData } from "@trigger.dev/sdk/ai"; + * import type { myChat } from "@/trigger/chat"; + * + * type MyClientData = InferChatClientData; + * ``` + */ +export type InferChatClientData = TTask extends Task< + string, + ChatTaskWirePayload, + any +> + ? TMetadata + : unknown; + +/** + * Extracts the UI message type from a chat task (wire payload `messages` items). + * + * @example + * ```ts + * import type { InferChatUIMessage } from "@trigger.dev/sdk/ai"; + * import type { myChat } from "@/trigger/chat"; + * + * type Msg = InferChatUIMessage; + * ``` + */ +export type InferChatUIMessage = TTask extends Task< + string, + ChatTaskWirePayload, + any +> + ? TUIM + : UIMessage; diff --git a/packages/trigger-sdk/src/v3/chat-client.ts b/packages/trigger-sdk/src/v3/chat-client.ts index c1a287fad..0c6562e7a 100644 --- a/packages/trigger-sdk/src/v3/chat-client.ts +++ b/packages/trigger-sdk/src/v3/chat-client.ts @@ -20,7 +20,7 @@ import type { Task } from "@trigger.dev/core/v3"; import type { UIMessage, UIMessageChunk } from "ai"; import { readUIMessageStream } from "ai"; import { ApiClient, SSEStreamSubscription, apiClientManager } from "@trigger.dev/core/v3"; -import type { ChatInputChunk, ChatTaskWirePayload } from "./ai.js"; +import type { ChatInputChunk, ChatTaskWirePayload } from "./ai-shared.js"; import { sessions } from "./sessions.js"; import { trigger } from "./shared.js"; diff --git a/packages/trigger-sdk/src/v3/chat-react.ts b/packages/trigger-sdk/src/v3/chat-react.ts index 3ca31e9d4..bf18c8f49 100644 --- a/packages/trigger-sdk/src/v3/chat-react.ts +++ b/packages/trigger-sdk/src/v3/chat-react.ts @@ -30,7 +30,7 @@ import { PENDING_MESSAGE_INJECTED_TYPE, type InferChatClientData, type InferChatUIMessage, -} from "./ai.js"; +} from "./ai-shared.js"; import type { UIMessage, ChatRequestOptions } from "ai"; /**