@truefoundry/assistant-ui-runtime 0.1.4 → 0.1.6-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +374 -190
  2. package/dist/index.d.ts +32 -29
  3. package/dist/index.js +334 -241
  4. package/dist/index.js.map +1 -1
  5. package/dist/plugins/truefoundry-agent-server-adapter/index.d.ts +30 -0
  6. package/dist/plugins/truefoundry-agent-server-adapter/index.js +198 -0
  7. package/dist/plugins/truefoundry-agent-server-adapter/index.js.map +1 -0
  8. package/dist/types-VUBzoJT2.d.ts +462 -0
  9. package/package.json +12 -4
  10. package/src/askUserQuestion.ts +3 -3
  11. package/src/collectPending.ts +1 -1
  12. package/src/convertTurnMessages.test.ts +141 -196
  13. package/src/convertTurnMessages.ts +131 -77
  14. package/src/createSubAgent.ts +1 -1
  15. package/src/draftAgentConfig.test.ts +26 -29
  16. package/src/extractTurnUserText.ts +1 -1
  17. package/src/foldPeerThreads.test.ts +1 -1
  18. package/src/foldPeerThreads.ts +3 -2
  19. package/src/index.ts +39 -4
  20. package/src/listPages.ts +21 -0
  21. package/src/loadSessionSnapshot.test.ts +9 -8
  22. package/src/loadSessionSnapshot.ts +9 -14
  23. package/src/mcpAuth.ts +6 -3
  24. package/src/messageCustomMetadata.ts +1 -1
  25. package/src/modelMessageContent.ts +1 -1
  26. package/src/modelMessageImageContent.test.ts +1 -1
  27. package/src/modelMessageImageContent.ts +7 -6
  28. package/src/plugins/truefoundry-agent-server-adapter/index.ts +285 -0
  29. package/src/private/agentSpec.ts +8 -3
  30. package/src/private/draftSessionBridge.ts +14 -13
  31. package/src/private/truefoundryDraftThreadListAdapter.test.ts +44 -49
  32. package/src/private/truefoundryDraftThreadListAdapter.ts +22 -16
  33. package/src/requiredActionInputs.ts +1 -1
  34. package/src/requiredActionsFromActiveUpdate.test.ts +1 -1
  35. package/src/server/eventUtils.ts +120 -0
  36. package/src/server/events.ts +246 -0
  37. package/src/server/index.ts +66 -0
  38. package/src/server/types.ts +313 -0
  39. package/src/sessionSnapshot.ts +1 -1
  40. package/src/sessions.ts +5 -21
  41. package/src/streamTurn.test.ts +175 -158
  42. package/src/streamTurn.ts +51 -57
  43. package/src/toolApproval.ts +4 -4
  44. package/src/toolResponse.ts +4 -4
  45. package/src/truefoundryExtras.ts +1 -1
  46. package/src/truefoundryOwnedSessionsThreadListAdapter.test.ts +26 -29
  47. package/src/truefoundryOwnedSessionsThreadListAdapter.ts +18 -23
  48. package/src/truefoundryThreadListAdapter.test.ts +16 -18
  49. package/src/truefoundryThreadListAdapter.ts +9 -9
  50. package/src/turnEventHelpers.ts +1 -1
  51. package/src/types.ts +2 -16
  52. package/src/useTrueFoundryAgentMessages.test.tsx +38 -70
  53. package/src/useTrueFoundryAgentMessages.ts +33 -45
  54. package/src/useTrueFoundryAgentRuntime.ts +11 -28
  55. package/src/private/bindDraftAgentSession.test.ts +0 -54
  56. package/src/private/bindDraftAgentSession.ts +0 -28
  57. package/src/private/getGatewayFromPrivateClient.ts +0 -13
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it, vi } from "vitest";
2
2
 
3
- import type { PrivateAgentSessionClient } from "truefoundry-gateway-sdk/agents/private";
3
+ import type { AgentChatServer, Session } from "../server/index.js";
4
4
 
5
5
  import { createTrueFoundryDraftThreadListAdapter } from "./truefoundryDraftThreadListAdapter.js";
6
6
  import type { AgentSpec } from "./agentSpec.js";
@@ -10,62 +10,57 @@ const defaultAgentSpec: AgentSpec = {
10
10
  instructions: "You are helpful.",
11
11
  };
12
12
 
13
- function mockDraft(id: string, title: string | undefined, updatedAt: string) {
13
+ function mockDraft(id: string, title: string | undefined, updatedAt: string): Session {
14
14
  return {
15
- type: "session/draft" as const,
16
15
  id,
17
16
  agentSpec: defaultAgentSpec,
18
17
  title,
19
- createdBySubject: { type: "user" as const, id: "u1" },
20
18
  createdAt: updatedAt,
21
19
  updatedAt,
20
+ isMutable: true,
22
21
  };
23
22
  }
24
23
 
25
- function mockDraftListPage(
26
- drafts: ReturnType<typeof mockDraft>[],
27
- nextPageToken?: string,
28
- ) {
24
+ function mockDraftListPage(drafts: Session[], nextPageToken?: string) {
29
25
  return {
30
26
  data: drafts,
31
- response: {
32
- pagination: {
33
- nextPageToken,
34
- limit: 20,
35
- },
36
- },
27
+ ...(nextPageToken != null ? { nextPageToken } : {}),
37
28
  };
38
29
  }
39
30
 
31
+ function mockServer(partial: Partial<AgentChatServer>): AgentChatServer {
32
+ return partial as AgentChatServer;
33
+ }
34
+
40
35
  describe("createTrueFoundryDraftThreadListAdapter", () => {
41
36
  it("lists draft sessions with pagination cursor", async () => {
42
- const listDraftSessions = vi.fn().mockResolvedValue(
37
+ const listSessions = vi.fn().mockResolvedValue(
43
38
  mockDraftListPage(
44
39
  [mockDraft("d1", "My draft", "2026-06-30T10:00:00.000Z")],
45
40
  "page-2",
46
41
  ),
47
42
  );
48
- const privateClient = {
49
- listDraftSessions,
50
- createDraftSession: vi.fn(),
51
- getDraftSession: vi.fn(),
52
- } as unknown as PrivateAgentSessionClient;
43
+ const server = mockServer({
44
+ listSessions,
45
+ createSession: vi.fn(),
46
+ getSession: vi.fn(),
47
+ });
53
48
 
54
49
  const adapter = createTrueFoundryDraftThreadListAdapter({
55
- privateClient,
50
+ server,
56
51
  defaultAgentSpec,
57
52
  });
58
53
 
59
54
  const result = await adapter.list();
60
55
 
61
- expect(listDraftSessions).toHaveBeenCalledWith(
56
+ expect(listSessions).toHaveBeenCalledWith(
62
57
  expect.objectContaining({
63
58
  limit: 20,
64
59
  pageToken: undefined,
65
60
  startTimestamp: expect.any(String),
66
61
  }),
67
62
  );
68
- expect(listDraftSessions).toHaveBeenCalledWith(
63
+ expect(listSessions).toHaveBeenCalledWith(
69
64
  expect.not.objectContaining({ agentName: expect.anything() }),
70
65
  );
71
66
  expect(result.threads).toEqual([
@@ -80,23 +75,23 @@ describe("createTrueFoundryDraftThreadListAdapter", () => {
80
75
  });
81
76
 
82
77
  it("creates a draft session on initialize", async () => {
83
- const createDraftSession = vi.fn().mockResolvedValue(
78
+ const createSession = vi.fn().mockResolvedValue(
84
79
  mockDraft("d-new", undefined, "2026-06-30T12:00:00.000Z"),
85
80
  );
86
- const privateClient = {
87
- listDraftSessions: vi.fn(),
88
- createDraftSession,
89
- getDraftSession: vi.fn(),
90
- } as unknown as PrivateAgentSessionClient;
81
+ const server = mockServer({
82
+ listSessions: vi.fn(),
83
+ createSession,
84
+ getSession: vi.fn(),
85
+ });
91
86
 
92
87
  const adapter = createTrueFoundryDraftThreadListAdapter({
93
- privateClient,
88
+ server,
94
89
  defaultAgentSpec,
95
90
  });
96
91
 
97
92
  const result = await adapter.initialize("local-thread-id");
98
93
 
99
- expect(createDraftSession).toHaveBeenCalledWith({ agentSpec: defaultAgentSpec });
94
+ expect(createSession).toHaveBeenCalledWith({ agentSpec: defaultAgentSpec });
100
95
  expect(result).toEqual({ remoteId: "d-new", externalId: undefined });
101
96
  });
102
97
 
@@ -104,47 +99,47 @@ describe("createTrueFoundryDraftThreadListAdapter", () => {
104
99
  const liveAgentSpec: AgentSpec = {
105
100
  model: { name: "anthropic/claude-opus-4-8" },
106
101
  instructions: "You are helpful.",
107
- mcpServers: [{ type: "truefoundry-mcp-registry", name: "github", enableTools: ["@all"] }],
108
- skills: [{ fqn: "acme/skill-a:1", preload: false }],
102
+ mcpServers: [{ id: "github", name: "github" }],
103
+ skills: [{ id: "skill-a", name: "skill-a" }],
109
104
  };
110
- const createDraftSession = vi.fn().mockResolvedValue(
105
+ const createSession = vi.fn().mockResolvedValue(
111
106
  mockDraft("d-new", undefined, "2026-06-30T12:00:00.000Z"),
112
107
  );
113
- const privateClient = {
114
- listDraftSessions: vi.fn(),
115
- createDraftSession,
116
- getDraftSession: vi.fn(),
117
- } as unknown as PrivateAgentSessionClient;
108
+ const server = mockServer({
109
+ listSessions: vi.fn(),
110
+ createSession,
111
+ getSession: vi.fn(),
112
+ });
118
113
 
119
114
  const adapter = createTrueFoundryDraftThreadListAdapter({
120
- privateClient,
115
+ server,
121
116
  defaultAgentSpec,
122
117
  getAgentSpec: () => liveAgentSpec,
123
118
  });
124
119
 
125
120
  await adapter.initialize("local-thread-id");
126
121
 
127
- expect(createDraftSession).toHaveBeenCalledWith({ agentSpec: liveAgentSpec });
122
+ expect(createSession).toHaveBeenCalledWith({ agentSpec: liveAgentSpec });
128
123
  });
129
124
 
130
125
  it("falls back to model name for title when draft has no title", async () => {
131
- const getDraftSession = vi.fn().mockResolvedValue(
126
+ const getSession = vi.fn().mockResolvedValue(
132
127
  mockDraft("d1", undefined, "2026-06-30T10:00:00.000Z"),
133
128
  );
134
- const privateClient = {
135
- listDraftSessions: vi.fn(),
136
- createDraftSession: vi.fn(),
137
- getDraftSession,
138
- } as unknown as PrivateAgentSessionClient;
129
+ const server = mockServer({
130
+ listSessions: vi.fn(),
131
+ createSession: vi.fn(),
132
+ getSession,
133
+ });
139
134
 
140
135
  const adapter = createTrueFoundryDraftThreadListAdapter({
141
- privateClient,
136
+ server,
142
137
  defaultAgentSpec,
143
138
  });
144
139
 
145
140
  const result = await adapter.fetch("d1");
146
141
 
147
- expect(getDraftSession).toHaveBeenCalledWith({ draftSessionId: "d1" });
142
+ expect(getSession).toHaveBeenCalledWith({ sessionId: "d1" });
148
143
  expect(result.title).toBe("anthropic/claude-sonnet-4-6");
149
144
  });
150
145
  });
@@ -1,52 +1,58 @@
1
1
  import type { RemoteThreadListAdapter } from "@assistant-ui/core";
2
- import type { PrivateAgentSessionClient } from "truefoundry-gateway-sdk/agents/private";
3
2
 
3
+ import type { AgentChatServer } from "../server/types.js";
4
4
  import { draftSessionTitle, type AgentSpec } from "./agentSpec.js";
5
5
  import { sessionListStartTimestamp } from "../sessionListStartTimestamp.js";
6
6
 
7
7
  const THREAD_LIST_PAGE_SIZE = 20;
8
8
 
9
9
  export function createTrueFoundryDraftThreadListAdapter(options: {
10
- privateClient: PrivateAgentSessionClient;
10
+ server: AgentChatServer;
11
11
  defaultAgentSpec: AgentSpec;
12
12
  getAgentSpec?: () => AgentSpec;
13
13
  }): RemoteThreadListAdapter {
14
- const { privateClient, defaultAgentSpec, getAgentSpec } = options;
14
+ const { server, defaultAgentSpec, getAgentSpec } = options;
15
15
 
16
16
  return {
17
17
  async list({ after } = {}) {
18
- const page = await privateClient.listDraftSessions({
18
+ const page = await server.listSessions({
19
19
  limit: THREAD_LIST_PAGE_SIZE,
20
20
  pageToken: after,
21
21
  startTimestamp: sessionListStartTimestamp(),
22
22
  });
23
- const threads = page.data.map((draft) => ({
24
- status: "regular" as const,
25
- remoteId: draft.id,
26
- title: draftSessionTitle(draft),
27
- lastMessageAt: new Date(draft.updatedAt),
28
- }));
23
+ const threads = page.data
24
+ .filter((session) => session.isMutable)
25
+ .map((draft) => ({
26
+ status: "regular" as const,
27
+ remoteId: draft.id,
28
+ title: draftSessionTitle({
29
+ title: draft.title,
30
+ agentSpec: draft.agentSpec ?? defaultAgentSpec,
31
+ }),
32
+ lastMessageAt: new Date(draft.updatedAt),
33
+ }));
29
34
  return {
30
35
  threads,
31
- nextCursor: page.response.pagination.nextPageToken ?? undefined,
36
+ nextCursor: page.nextPageToken ?? undefined,
32
37
  };
33
38
  },
34
39
 
35
40
  async initialize(_threadId: string) {
36
- const draft = await privateClient.createDraftSession({
41
+ const draft = await server.createSession({
37
42
  agentSpec: getAgentSpec?.() ?? defaultAgentSpec,
38
43
  });
39
44
  return { remoteId: draft.id, externalId: undefined };
40
45
  },
41
46
 
42
47
  async fetch(remoteId) {
43
- const draft = await privateClient.getDraftSession({
44
- draftSessionId: remoteId,
45
- });
48
+ const draft = await server.getSession({ sessionId: remoteId });
46
49
  return {
47
50
  status: "regular" as const,
48
51
  remoteId: draft.id,
49
- title: draftSessionTitle(draft),
52
+ title: draftSessionTitle({
53
+ title: draft.title,
54
+ agentSpec: draft.agentSpec ?? defaultAgentSpec,
55
+ }),
50
56
  lastMessageAt: new Date(draft.updatedAt),
51
57
  };
52
58
  },
@@ -3,7 +3,7 @@ import type {
3
3
  TurnInputItem,
4
4
  UserToolApprovalEvent,
5
5
  UserToolResponseEvent,
6
- } from "truefoundry-gateway-sdk/agents";
6
+ } from "./server/index.js";
7
7
 
8
8
  import { ROOT_THREAD_ID } from "./constants.js";
9
9
  import {
@@ -64,7 +64,7 @@ describe("requiredActionsFromActiveUpdate", () => {
64
64
  };
65
65
 
66
66
  const actions = requiredActionsFromActiveUpdate(update);
67
- expect(actions.map((action) => action.type)).toEqual([
67
+ expect(actions?.map((action) => action.type)).toEqual([
68
68
  "tool.approval_required",
69
69
  "tool.response_required",
70
70
  ]);
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Local implementations of streaming delta helpers.
3
+ * Formerly imported from truefoundry-gateway-sdk/agents.
4
+ */
5
+
6
+ import type {
7
+ DeltaEvents,
8
+ ModelMessageDeltaEvent,
9
+ ModelMessageEvent,
10
+ ToolCall,
11
+ ToolInfo,
12
+ TurnEvent,
13
+ TurnStreamingEvent,
14
+ } from "./events.js";
15
+
16
+ /** True for `.delta` streaming events. */
17
+ export function isEventDelta(event: TurnStreamingEvent): event is DeltaEvents {
18
+ return typeof event.type === "string" && event.type.endsWith(".delta");
19
+ }
20
+
21
+ /**
22
+ * Merge `delta` into `base` in place (same `id` required).
23
+ * Currently handles `model.message.delta` → `model.message`.
24
+ */
25
+ export function mergeEventDelta(base: TurnEvent, delta: DeltaEvents): void {
26
+ if (base.id !== delta.id) {
27
+ throw new Error(
28
+ `Cannot merge delta into a different event: base id "${base.id}" != delta id "${delta.id}".`,
29
+ );
30
+ }
31
+ if (delta.type === "model.message.delta" && base.type === "model.message") {
32
+ mergeModelMessageDelta(base, delta);
33
+ }
34
+ }
35
+
36
+ function asToolInfo(value: unknown): ToolInfo | undefined {
37
+ if (value == null || typeof value !== "object") {
38
+ return undefined;
39
+ }
40
+ return value as ToolInfo;
41
+ }
42
+
43
+ function mergeModelMessageDelta(
44
+ base: ModelMessageEvent,
45
+ delta: ModelMessageDeltaEvent,
46
+ ): void {
47
+ if (delta.content) {
48
+ if (
49
+ base.content === undefined ||
50
+ base.content === null ||
51
+ typeof base.content === "string"
52
+ ) {
53
+ base.content = (base.content ?? "") + delta.content;
54
+ } else {
55
+ const last = base.content[base.content.length - 1];
56
+ if (last && last.type === "text") {
57
+ last.text += delta.content;
58
+ } else {
59
+ base.content.push({ type: "text", text: delta.content });
60
+ }
61
+ }
62
+ }
63
+
64
+ if (delta.refusal) {
65
+ base.refusal = (base.refusal ?? "") + delta.refusal;
66
+ }
67
+
68
+ if (delta.toolCalls) {
69
+ base.toolCalls ??= [];
70
+ for (const d of delta.toolCalls) {
71
+ let tc: ToolCall | undefined = base.toolCalls[d.index];
72
+ if (tc === undefined) {
73
+ const toolInfo = asToolInfo(d.toolInfo);
74
+ tc = {
75
+ id: d.id ?? "",
76
+ type: d.type ?? "function",
77
+ function: {
78
+ name: d.function?.name ?? "",
79
+ arguments: "",
80
+ },
81
+ ...(toolInfo != null ? { toolInfo } : {}),
82
+ };
83
+ base.toolCalls[d.index] = tc;
84
+ }
85
+ if (d.id) {
86
+ tc.id = d.id;
87
+ }
88
+ if (d.type) {
89
+ tc.type = d.type;
90
+ }
91
+ if (d.function?.name) {
92
+ tc.function.name = d.function.name;
93
+ }
94
+ if (d.function?.arguments) {
95
+ tc.function.arguments += d.function.arguments;
96
+ }
97
+ const toolInfo = asToolInfo(d.toolInfo);
98
+ if (toolInfo != null) {
99
+ tc.toolInfo = toolInfo;
100
+ }
101
+ if (d.providerSpecificFields) {
102
+ tc.providerSpecificFields = {
103
+ ...(tc.providerSpecificFields ?? {}),
104
+ ...d.providerSpecificFields,
105
+ };
106
+ }
107
+ }
108
+ }
109
+
110
+ if (delta.finishReason) {
111
+ base.finishReason = delta.finishReason;
112
+ }
113
+ if (delta.reasoningContent) {
114
+ base.reasoningContent =
115
+ (base.reasoningContent ?? "") + delta.reasoningContent;
116
+ }
117
+ if (delta.usage) {
118
+ base.usage = delta.usage;
119
+ }
120
+ }
@@ -0,0 +1,246 @@
1
+ /**
2
+ * Runtime-owned turn/stream event protocol.
3
+ *
4
+ * Hosts must emit events matching these shapes. The TFY adapter maps
5
+ * truefoundry-gateway-sdk events 1:1 onto these types.
6
+ */
7
+
8
+ import type { TurnInputItem, TurnState } from "./types.js";
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Tool call shapes
12
+ // ---------------------------------------------------------------------------
13
+
14
+ export interface ToolCallFunction {
15
+ name: string;
16
+ arguments: string;
17
+ }
18
+
19
+ export type ToolInfo =
20
+ | { type: "truefoundry-system"; name: string }
21
+ | { type: "mcp"; serverId: string; serverName: string; name: string }
22
+ | { type: string; name?: string; [key: string]: unknown };
23
+
24
+ export interface ToolCall {
25
+ id: string;
26
+ type: "function";
27
+ function: ToolCallFunction;
28
+ toolInfo?: ToolInfo;
29
+ providerSpecificFields?: Record<string, unknown>;
30
+ }
31
+
32
+ /** Ref used by approval/response-required events. */
33
+ export interface ToolCallRef {
34
+ id: string;
35
+ sourceEventId: string;
36
+ }
37
+
38
+ export interface ChunkDeltaToolCall {
39
+ index: number;
40
+ id?: string;
41
+ type?: "function";
42
+ function?: { name?: string; arguments?: string };
43
+ toolInfo?: ToolInfo;
44
+ providerSpecificFields?: Record<string, unknown>;
45
+ }
46
+
47
+ // ---------------------------------------------------------------------------
48
+ // Content events
49
+ // ---------------------------------------------------------------------------
50
+
51
+ export type ModelMessageContentPart =
52
+ | { type: "text"; text: string }
53
+ | { type: "refusal"; refusal: string }
54
+ | { type: "image_url"; image_url: { url: string } };
55
+
56
+ export interface ModelMessageEvent {
57
+ type: "model.message";
58
+ id: string;
59
+ threadId: string;
60
+ content?: string | ModelMessageContentPart[] | null;
61
+ name?: string;
62
+ refusal?: string | null;
63
+ reasoningContent?: string;
64
+ toolCalls?: ToolCall[];
65
+ finishReason?: string | null;
66
+ createdAt: string;
67
+ usage?: unknown;
68
+ }
69
+
70
+ export interface ModelMessageDeltaEvent {
71
+ type: "model.message.delta";
72
+ id: string;
73
+ threadId: string;
74
+ content?: string | null;
75
+ refusal?: string | null;
76
+ reasoningContent?: string;
77
+ toolCalls?: ChunkDeltaToolCall[];
78
+ finishReason?: string | null;
79
+ createdAt?: string;
80
+ usage?: unknown;
81
+ /** Extended content-block deltas (image streaming). */
82
+ contentBlocks?: Array<{
83
+ index: number;
84
+ delta:
85
+ | { type: "text"; text?: string }
86
+ | { type: "image_url"; image_url?: { url?: string } };
87
+ }>;
88
+ content_blocks?: Array<{
89
+ index: number;
90
+ delta:
91
+ | { type: "text"; text?: string }
92
+ | { type: "image_url"; image_url?: { url?: string } };
93
+ }>;
94
+ }
95
+
96
+ export interface ToolResponseEvent {
97
+ type: "tool.response";
98
+ id: string;
99
+ threadId: string;
100
+ toolCallId: string;
101
+ content: string;
102
+ createdAt: string;
103
+ }
104
+
105
+ export interface ToolApprovalRequiredEvent {
106
+ type: "tool.approval_required";
107
+ id: string;
108
+ createdAt: string;
109
+ threadId: string;
110
+ toolCalls: ToolCallRef[];
111
+ }
112
+
113
+ export interface ToolResponseRequiredEvent {
114
+ type: "tool.response_required";
115
+ id: string;
116
+ createdAt: string;
117
+ threadId: string;
118
+ toolCalls: ToolCallRef[];
119
+ }
120
+
121
+ export interface AgentInfo {
122
+ type?: string;
123
+ name: string;
124
+ input: string;
125
+ model?: string;
126
+ }
127
+
128
+ export interface AgentParent {
129
+ threadId: string;
130
+ toolCallId: string;
131
+ }
132
+
133
+ export interface ThreadCreatedEvent {
134
+ type: "thread.created";
135
+ id: string;
136
+ threadId: string;
137
+ title: string;
138
+ agentInfo: AgentInfo;
139
+ parent: AgentParent;
140
+ createdAt: string;
141
+ }
142
+
143
+ export interface ThreadDoneEvent {
144
+ type: "thread.done";
145
+ id: string;
146
+ threadId: string;
147
+ title?: string;
148
+ createdAt: string;
149
+ state?: unknown;
150
+ }
151
+
152
+ export interface McpServerAuthInfo {
153
+ id: string;
154
+ name: string;
155
+ authUrl: string;
156
+ }
157
+
158
+ export interface McpAuthRequiredEvent {
159
+ type: "mcp.auth_required";
160
+ id: string;
161
+ createdAt: string;
162
+ threadId?: string | null;
163
+ mcpServers: McpServerAuthInfo[];
164
+ }
165
+
166
+ export interface SandboxCreatedEvent {
167
+ type: "sandbox.created";
168
+ id: string;
169
+ createdAt: string;
170
+ sandboxId: string;
171
+ threadId: string | null;
172
+ }
173
+
174
+ export interface McpInitializeEvent {
175
+ type: "mcp.initialize";
176
+ id: string;
177
+ createdAt: string;
178
+ threadId: string | null;
179
+ [key: string]: unknown;
180
+ }
181
+
182
+ // ---------------------------------------------------------------------------
183
+ // Turn lifecycle events
184
+ // ---------------------------------------------------------------------------
185
+
186
+ export interface TurnCreatedEvent {
187
+ type: "turn.created";
188
+ id: string;
189
+ turnId: string;
190
+ previousTurnId?: string | null;
191
+ input?: TurnInputItem[];
192
+ state?: { status: "running" };
193
+ createdAt: string;
194
+ threadId?: string | null;
195
+ }
196
+
197
+ export interface TurnDoneEvent {
198
+ type: "turn.done";
199
+ id: string;
200
+ state: Exclude<TurnState, { status: "running" }>;
201
+ createdAt: string;
202
+ threadId?: string | null;
203
+ }
204
+
205
+ // ---------------------------------------------------------------------------
206
+ // Unions
207
+ // ---------------------------------------------------------------------------
208
+
209
+ /** Events stored in fold buckets (non-delta, non-turn-lifecycle). */
210
+ export type TurnEvent =
211
+ | ModelMessageEvent
212
+ | ToolResponseEvent
213
+ | ThreadCreatedEvent
214
+ | ThreadDoneEvent
215
+ | McpAuthRequiredEvent
216
+ | McpInitializeEvent
217
+ | SandboxCreatedEvent
218
+ | ToolApprovalRequiredEvent
219
+ | ToolResponseRequiredEvent;
220
+
221
+ /** Full streaming event union (includes deltas + turn lifecycle). */
222
+ export type TurnStreamingEvent =
223
+ | TurnEvent
224
+ | ModelMessageDeltaEvent
225
+ | TurnCreatedEvent
226
+ | TurnDoneEvent;
227
+
228
+ export type ActionRequiredEvent =
229
+ | ToolApprovalRequiredEvent
230
+ | ToolResponseRequiredEvent
231
+ | McpAuthRequiredEvent;
232
+
233
+ export interface TurnStreamData<
234
+ TStreamEvent extends TurnStreamingEvent = TurnStreamingEvent,
235
+ > {
236
+ sequenceNumber: number;
237
+ event: TStreamEvent;
238
+ }
239
+
240
+ /** Session-level event item from `listEvents`. */
241
+ export interface SessionEventItem {
242
+ turnId: string;
243
+ event: TurnCreatedEvent | TurnDoneEvent | TurnEvent;
244
+ }
245
+
246
+ export type DeltaEvents = ModelMessageDeltaEvent;