Skip to content

Commit a9b0fea

Browse files
committed
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).
1 parent 7a4722a commit a9b0fea

3 files changed

Lines changed: 126 additions & 2 deletions

File tree

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/**
2+
* Browser-safe primitives shared between `@trigger.dev/sdk/ai` (server) and
3+
* `@trigger.dev/sdk/chat` / `@trigger.dev/sdk/chat/react` (client).
4+
*
5+
* This module exists to keep `ai.ts` reachable only from the server graph.
6+
* `ai.ts` weighs in at ~7000 lines and statically imports the agent-skills
7+
* runtime (which uses `node:child_process` / `node:fs/promises`). When a
8+
* browser bundle imports a runtime value from `ai.ts` — historically the
9+
* `PENDING_MESSAGE_INJECTED_TYPE` constant in `chat-react.ts` — the bundler
10+
* traces `ai.ts`'s entire module graph into the client chunk and hits the
11+
* `node:` builtins, which Turbopack rejects outright (and webpack flags as
12+
* a "Critical dependency" warning).
13+
*
14+
* Anything in this file MUST stay free of `node:*` imports and free of any
15+
* import from `ai.ts`.
16+
*/
17+
18+
import type { Task, AnyTask } from "@trigger.dev/core/v3";
19+
import type { UIMessage } from "ai";
20+
21+
/**
22+
* Message-part `type` value for the pending-message data part the agent
23+
* injects when a follow-up message arrives mid-turn.
24+
*/
25+
export const PENDING_MESSAGE_INJECTED_TYPE = "data-pending-message-injected" as const;
26+
27+
/**
28+
* The wire payload shape sent by `TriggerChatTransport`.
29+
* Uses `metadata` to match the AI SDK's `ChatRequestOptions` field name.
30+
*/
31+
export type ChatTaskWirePayload<TMessage extends UIMessage = UIMessage, TMetadata = unknown> = {
32+
messages: TMessage[];
33+
chatId: string;
34+
trigger: "submit-message" | "regenerate-message" | "preload" | "close" | "action";
35+
messageId?: string;
36+
metadata?: TMetadata;
37+
/** Custom action payload when `trigger` is `"action"`. Validated against `actionSchema` on the backend. */
38+
action?: unknown;
39+
/** Whether this run is continuing an existing chat whose previous run ended. */
40+
continuation?: boolean;
41+
/** The run ID of the previous run (only set when `continuation` is true). */
42+
previousRunId?: string;
43+
/** Override idle timeout for this run (seconds). Set by transport.preload(). */
44+
idleTimeoutInSeconds?: number;
45+
/**
46+
* The friendlyId of the Session primitive backing this chat. The
47+
* transport opens (or lazy-creates) the session with
48+
* `externalId = chatId` on first message, then sends this friendlyId
49+
* through to the run so the agent can attach to `.in` / `.out`
50+
* without needing to round-trip through the control plane again.
51+
* Optional for backward-compat while the migration is in flight;
52+
* required once the legacy run-scoped stream path is removed.
53+
*/
54+
sessionId?: string;
55+
/**
56+
* Client-side `chat.store` value sent by the transport. Applied at turn
57+
* start before `run()` fires, overwriting any in-memory store value on the
58+
* agent (last-write-wins).
59+
*
60+
* The transport queues this via `setStore` / `applyStorePatch` and flushes
61+
* it with the next `sendMessage`. On the agent you typically don't read
62+
* this directly — it's applied into `chat.store` transparently.
63+
*/
64+
incomingStore?: unknown;
65+
};
66+
67+
/**
68+
* One chunk on the chat input stream. `kind` discriminates the variants —
69+
* a single ordered stream now carries all the signals the old three-stream
70+
* split did (`chat-messages`, `chat-stop`, plus action messages piggybacked
71+
* on `chat-messages`).
72+
*/
73+
export type ChatInputChunk<TMessage extends UIMessage = UIMessage, TMetadata = unknown> =
74+
| {
75+
kind: "message";
76+
/**
77+
* Full wire payload for a new user message or regeneration. Mirrors
78+
* what the legacy `chat-messages` input stream carried.
79+
*/
80+
payload: ChatTaskWirePayload<TMessage, TMetadata>;
81+
}
82+
| {
83+
kind: "stop";
84+
/** Optional human-readable reason. Maps to the legacy `chat-stop` record. */
85+
message?: string;
86+
};
87+
88+
/**
89+
* Extracts the client-data (`metadata`) type from a chat task.
90+
*
91+
* @example
92+
* ```ts
93+
* import type { InferChatClientData } from "@trigger.dev/sdk/ai";
94+
* import type { myChat } from "@/trigger/chat";
95+
*
96+
* type MyClientData = InferChatClientData<typeof myChat>;
97+
* ```
98+
*/
99+
export type InferChatClientData<TTask extends AnyTask> = TTask extends Task<
100+
string,
101+
ChatTaskWirePayload<any, infer TMetadata>,
102+
any
103+
>
104+
? TMetadata
105+
: unknown;
106+
107+
/**
108+
* Extracts the UI message type from a chat task (wire payload `messages` items).
109+
*
110+
* @example
111+
* ```ts
112+
* import type { InferChatUIMessage } from "@trigger.dev/sdk/ai";
113+
* import type { myChat } from "@/trigger/chat";
114+
*
115+
* type Msg = InferChatUIMessage<typeof myChat>;
116+
* ```
117+
*/
118+
export type InferChatUIMessage<TTask extends AnyTask> = TTask extends Task<
119+
string,
120+
ChatTaskWirePayload<infer TUIM extends UIMessage, any>,
121+
any
122+
>
123+
? TUIM
124+
: UIMessage;

packages/trigger-sdk/src/v3/chat-client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import type { Task } from "@trigger.dev/core/v3";
2020
import type { UIMessage, UIMessageChunk } from "ai";
2121
import { readUIMessageStream } from "ai";
2222
import { ApiClient, SSEStreamSubscription, apiClientManager } from "@trigger.dev/core/v3";
23-
import type { ChatInputChunk, ChatTaskWirePayload } from "./ai.js";
23+
import type { ChatInputChunk, ChatTaskWirePayload } from "./ai-shared.js";
2424
import { sessions } from "./sessions.js";
2525
import { trigger } from "./shared.js";
2626

packages/trigger-sdk/src/v3/chat-react.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import {
3030
PENDING_MESSAGE_INJECTED_TYPE,
3131
type InferChatClientData,
3232
type InferChatUIMessage,
33-
} from "./ai.js";
33+
} from "./ai-shared.js";
3434
import type { UIMessage, ChatRequestOptions } from "ai";
3535

3636
/**

0 commit comments

Comments
 (0)