@truefoundry/assistant-ui-runtime 0.1.5 → 0.1.6

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 (70) hide show
  1. package/README.md +198 -401
  2. package/dist/chunk-3A2EPLQG.js +93 -0
  3. package/dist/chunk-3A2EPLQG.js.map +1 -0
  4. package/dist/chunk-Q2SHKMLM.js +270 -0
  5. package/dist/chunk-Q2SHKMLM.js.map +1 -0
  6. package/dist/index.d.ts +24 -29
  7. package/dist/index.js +264 -237
  8. package/dist/index.js.map +1 -1
  9. package/dist/plugins/truefoundry-agent-server-adapter/index.d.ts +162 -0
  10. package/dist/plugins/truefoundry-agent-server-adapter/index.js +19 -0
  11. package/dist/plugins/truefoundry-agent-server-adapter/index.js.map +1 -0
  12. package/dist/server/index.d.ts +17 -0
  13. package/dist/server/index.js +9 -0
  14. package/dist/server/index.js.map +1 -0
  15. package/dist/types-BfiFf8O1.d.ts +468 -0
  16. package/package.json +19 -6
  17. package/src/askUserQuestion.ts +3 -3
  18. package/src/buildEditedUserMessageContent.test.ts +2 -2
  19. package/src/collectPending.ts +1 -1
  20. package/src/convertTurnMessages.test.ts +141 -196
  21. package/src/convertTurnMessages.ts +130 -76
  22. package/src/createSubAgent.ts +1 -1
  23. package/src/draftAgentConfig.test.ts +26 -29
  24. package/src/extractTurnUserText.ts +1 -1
  25. package/src/foldPeerThreads.test.ts +1 -1
  26. package/src/foldPeerThreads.ts +3 -2
  27. package/src/index.ts +80 -4
  28. package/src/listPages.ts +21 -0
  29. package/src/loadSessionSnapshot.test.ts +9 -8
  30. package/src/loadSessionSnapshot.ts +9 -14
  31. package/src/mcpAuth.ts +6 -3
  32. package/src/messageCustomMetadata.ts +1 -1
  33. package/src/modelMessageContent.ts +1 -1
  34. package/src/modelMessageImageContent.test.ts +1 -1
  35. package/src/modelMessageImageContent.ts +7 -6
  36. package/src/plugins/truefoundry-agent-server-adapter/README.md +178 -0
  37. package/src/plugins/truefoundry-agent-server-adapter/guards.test.ts +113 -0
  38. package/src/plugins/truefoundry-agent-server-adapter/guards.ts +130 -0
  39. package/src/plugins/truefoundry-agent-server-adapter/index.ts +359 -0
  40. package/src/plugins/truefoundry-agent-server-adapter/types.ts +135 -0
  41. package/src/plugins/truefoundry-agent-server-adapter/types.typecheck.ts +164 -0
  42. package/src/private/agentSpec.ts +8 -3
  43. package/src/private/draftSessionBridge.ts +14 -13
  44. package/src/private/truefoundryDraftThreadListAdapter.test.ts +44 -49
  45. package/src/private/truefoundryDraftThreadListAdapter.ts +22 -16
  46. package/src/requiredActionInputs.ts +1 -1
  47. package/src/requiredActionsFromActiveUpdate.test.ts +1 -1
  48. package/src/server/eventUtils.ts +120 -0
  49. package/src/server/events.ts +246 -0
  50. package/src/server/index.ts +66 -0
  51. package/src/server/types.ts +319 -0
  52. package/src/sessionSnapshot.ts +1 -1
  53. package/src/sessions.ts +5 -21
  54. package/src/streamTurn.test.ts +172 -155
  55. package/src/streamTurn.ts +51 -48
  56. package/src/toolApproval.ts +4 -4
  57. package/src/toolResponse.ts +4 -4
  58. package/src/truefoundryExtras.ts +1 -1
  59. package/src/truefoundryOwnedSessionsThreadListAdapter.test.ts +26 -29
  60. package/src/truefoundryOwnedSessionsThreadListAdapter.ts +18 -23
  61. package/src/truefoundryThreadListAdapter.test.ts +16 -18
  62. package/src/truefoundryThreadListAdapter.ts +7 -7
  63. package/src/turnEventHelpers.ts +1 -1
  64. package/src/types.ts +2 -16
  65. package/src/useTrueFoundryAgentMessages.test.tsx +38 -70
  66. package/src/useTrueFoundryAgentMessages.ts +32 -44
  67. package/src/useTrueFoundryAgentRuntime.ts +11 -28
  68. package/src/private/bindDraftAgentSession.test.ts +0 -54
  69. package/src/private/bindDraftAgentSession.ts +0 -28
  70. package/src/private/getGatewayFromPrivateClient.ts +0 -13
package/src/streamTurn.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import type {
2
- AgentSession,
3
- Turn,
2
+ AgentChatServer,
3
+ PreviousTurnIdInput,
4
4
  TurnInputItem,
5
- } from "truefoundry-gateway-sdk/agents";
6
- import type { TruefoundryGatewayApi } from "truefoundry-gateway-sdk";
5
+ } from "./server/types.js";
6
+ import type { TurnStreamData } from "./server/events.js";
7
7
 
8
8
  import {
9
9
  streamTurnEvents,
@@ -18,11 +18,11 @@ export type StreamTurnOptions = {
18
18
  resumeMcpAuth?: boolean;
19
19
  inputs?: RequiredActionInput[];
20
20
  /**
21
- * Branch anchor for `prepareTurn`. Omit for `"auto"`. Pass `"none"` for a fresh
22
- * root turn — sent as `previous_turn_id: "none"` on the wire.
21
+ * Branch anchor for prepareAndExecuteTurn. Omit for `"auto"`. Pass `"none"` for a fresh
22
+ * root turn.
23
23
  */
24
- previousTurnId?: TruefoundryGatewayApi.PreviousTurnIdInput;
25
- /** Extra headers for the createTurn request (`execute` request options). */
24
+ previousTurnId?: PreviousTurnIdInput;
25
+ /** Extra headers for the turn request. */
26
26
  headers?: Record<string, string>;
27
27
  };
28
28
 
@@ -36,9 +36,13 @@ function buildTurnInput(options: StreamTurnOptions): TurnInputItem[] {
36
36
  return [{ type: "user.message", content: options.userMessage ?? "" }];
37
37
  }
38
38
 
39
- function bindAbort(session: AgentSession, abortSignal: AbortSignal): () => void {
39
+ function bindAbort(
40
+ server: AgentChatServer,
41
+ sessionId: string,
42
+ abortSignal: AbortSignal,
43
+ ): () => void {
40
44
  const onAbort = () => {
41
- void session.cancel().catch(() => undefined);
45
+ void server.cancelSession({ sessionId }).catch(() => undefined);
42
46
  };
43
47
  if (abortSignal.aborted) {
44
48
  onAbort();
@@ -49,63 +53,50 @@ function bindAbort(session: AgentSession, abortSignal: AbortSignal): () => void
49
53
  }
50
54
 
51
55
  export async function* streamTurnContent(
52
- session: AgentSession,
56
+ server: AgentChatServer,
57
+ sessionId: string,
53
58
  foldState: PeerThreadFoldState,
54
59
  options: StreamTurnOptions,
55
60
  abortSignal: AbortSignal,
56
61
  groupRootBaseline?: readonly string[],
57
62
  /**
58
- * Called once with the gateway-assigned turn ID as soon as it becomes
59
- * available (after the first `turn.created` SSE event). Use this to
60
- * reconcile the locally-generated optimistic ID with the real gateway ID
61
- * so that edit/retry can find the turn in `buildSnapshotBeforeTurn`.
63
+ * Called once with the turn ID as soon as it becomes available (first
64
+ * `turn.created` SSE event). Use this to reconcile the locally-generated
65
+ * optimistic ID with the real turn ID.
62
66
  */
63
67
  onTurnIdAvailable?: (turnId: string) => void,
64
68
  ): AsyncGenerator<TurnStreamUpdate> {
65
- const turn = session.prepareTurn({
66
- input: buildTurnInput(options),
67
- previousTurnId: options.previousTurnId ?? "auto",
68
- });
69
-
70
- const onAbort = bindAbort(session, abortSignal);
69
+ const onAbort = bindAbort(server, sessionId, abortSignal);
71
70
  if (abortSignal.aborted) {
72
71
  return;
73
72
  }
74
73
 
75
74
  let turnIdNotified = false;
76
- const notifyTurnIdIfAvailable = () => {
77
- if (!turnIdNotified && turn.id != null) {
78
- onTurnIdAvailable?.(turn.id);
75
+ const notifyTurnId = (turnId: string) => {
76
+ if (!turnIdNotified) {
77
+ onTurnIdAvailable?.(turnId);
79
78
  turnIdNotified = true;
80
79
  }
81
80
  };
82
81
 
82
+ const stream: AsyncIterable<TurnStreamData> = server.prepareAndExecuteTurn({
83
+ sessionId,
84
+ input: buildTurnInput(options),
85
+ previousTurnId: options.previousTurnId ?? "auto",
86
+ abortSignal,
87
+ ...(options.headers != null ? { headers: options.headers } : {}),
88
+ });
89
+
83
90
  try {
84
91
  for await (const update of streamTurnEvents(
85
- turn.execute(
86
- { stream: true },
87
- {
88
- abortSignal,
89
- ...(options.headers != null ? { headers: options.headers } : {}),
90
- },
91
- ),
92
+ stream,
92
93
  foldState,
93
94
  groupRootBaseline,
95
+ notifyTurnId,
94
96
  )) {
95
- // After the first `turn.created` event, `turn.id` is set.
96
- // Notify BEFORE yielding so the caller can update its tracking
97
- // before the snapshot is written with the stream update.
98
- notifyTurnIdIfAvailable();
99
97
  yield update;
100
98
  }
101
- // Handle streams that complete without yielding any content.
102
- notifyTurnIdIfAvailable();
103
99
  } catch (error) {
104
- // Error streams often throw on `turn.done` (status=error) without ever
105
- // yielding content (e.g. model not servable). `turn.id` is still set
106
- // after `turn.created` — notify so edit/retry can resolve the turn.
107
- // Same for AbortError after create: keep local ids aligned with gateway.
108
- notifyTurnIdIfAvailable();
109
100
  if (error instanceof Error && error.name === "AbortError") {
110
101
  return;
111
102
  }
@@ -117,14 +108,22 @@ export async function* streamTurnContent(
117
108
 
118
109
  /** TODO: wire `afterSequenceNumber` from the last ingested stream event to skip replay on reconnect. */
119
110
  export async function* resumeTurnStream(
120
- turn: Turn,
111
+ server: AgentChatServer,
112
+ sessionId: string,
113
+ turnId: string,
121
114
  foldState: PeerThreadFoldState,
122
115
  abortSignal: AbortSignal,
123
116
  afterSequenceNumber?: number,
124
117
  groupRootBaseline?: readonly string[],
125
118
  ): AsyncGenerator<TurnStreamUpdate> {
119
+ if (server.subscribeToTurn == null) {
120
+ throw new Error(
121
+ "resumeTurnStream requires AgentChatServer.subscribeToTurn",
122
+ );
123
+ }
124
+
126
125
  const onAbort = () => {
127
- void turn.session.cancel().catch(() => undefined);
126
+ void server.cancelSession({ sessionId }).catch(() => undefined);
128
127
  };
129
128
  if (abortSignal.aborted) {
130
129
  onAbort();
@@ -134,10 +133,14 @@ export async function* resumeTurnStream(
134
133
 
135
134
  try {
136
135
  yield* streamTurnEvents(
137
- turn.stream(
138
- afterSequenceNumber != null ? { afterSequenceNumber } : {},
139
- { abortSignal },
140
- ),
136
+ server.subscribeToTurn({
137
+ sessionId,
138
+ turnId,
139
+ ...(afterSequenceNumber != null
140
+ ? { afterSequenceNumber }
141
+ : {}),
142
+ abortSignal,
143
+ }),
141
144
  foldState,
142
145
  groupRootBaseline,
143
146
  );
@@ -8,7 +8,7 @@ import type {
8
8
  ToolApprovalRequiredEvent,
9
9
  Turn,
10
10
  UserToolApprovalEvent,
11
- } from "truefoundry-gateway-sdk/agents";
11
+ } from "./server/index.js";
12
12
 
13
13
  import { ROOT_THREAD_ID } from "./constants.js";
14
14
  import type { ToolApprovalMessageCustomMetadata } from "./messageCustomMetadata.js";
@@ -148,10 +148,10 @@ export function findApprovalRequiredInTurn(
148
148
  if (turn.state.status !== "done") {
149
149
  return undefined;
150
150
  }
151
- return turn.state.requiredActions?.find(
152
- (action): action is ToolApprovalRequiredEvent =>
153
- action.type === "tool.approval_required",
151
+ const found = turn.state.requiredActions?.find(
152
+ (action) => action.type === "tool.approval_required",
154
153
  );
154
+ return found?.type === "tool.approval_required" ? found : undefined;
155
155
  }
156
156
 
157
157
  function toolCallPartHasPendingApproval(part: ToolCallPart): boolean {
@@ -9,7 +9,7 @@ import type {
9
9
  Turn,
10
10
  TurnInputItem,
11
11
  UserToolResponseEvent,
12
- } from "truefoundry-gateway-sdk/agents";
12
+ } from "./server/index.js";
13
13
 
14
14
  import { ROOT_THREAD_ID } from "./constants.js";
15
15
  import {
@@ -85,10 +85,10 @@ export function findResponseRequiredInTurn(
85
85
  if (turn.state.status !== "done") {
86
86
  return undefined;
87
87
  }
88
- return turn.state.requiredActions?.find(
89
- (action): action is ToolResponseRequiredEvent =>
90
- action.type === "tool.response_required",
88
+ const found = turn.state.requiredActions?.find(
89
+ (action) => action.type === "tool.response_required",
91
90
  );
91
+ return found?.type === "tool.response_required" ? found : undefined;
92
92
  }
93
93
 
94
94
  function applyToolResponseToToolCall(
@@ -1,5 +1,5 @@
1
1
  import { createRuntimeExtras } from "@assistant-ui/core/internal";
2
- import type { McpAuthRequiredEvent } from "truefoundry-gateway-sdk/agents";
2
+ import type { McpAuthRequiredEvent } from "./server/index.js";
3
3
 
4
4
  import type { AgentSpec, AgentSpecUpdate } from "./private/agentSpec.js";
5
5
  import type { PendingApproval, PendingToolResponse } from "./collectPending.js";
@@ -1,53 +1,54 @@
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 { createTrueFoundryOwnedSessionsThreadListAdapter } from "./truefoundryOwnedSessionsThreadListAdapter.js";
6
6
 
7
- function mockNamedSession(id: string, title: string, updatedAt: string) {
7
+ function mockNamedSession(id: string, title: string, updatedAt: string): Session {
8
8
  return {
9
- type: "session" as const,
10
9
  id,
11
10
  agentName: "my-agent",
12
11
  title,
13
- createdBySubject: { type: "user" as const, id: "u1" },
14
12
  createdAt: updatedAt,
15
13
  updatedAt,
14
+ isMutable: false,
16
15
  };
17
16
  }
18
17
 
19
- function mockDraftSession(id: string, title: string | undefined, updatedAt: string) {
18
+ function mockDraftSession(
19
+ id: string,
20
+ title: string | undefined,
21
+ updatedAt: string,
22
+ ): Session {
20
23
  return {
21
- type: "session/draft" as const,
22
24
  id,
23
25
  agentSpec: { model: { name: "anthropic/claude-sonnet-4-6" } },
24
26
  title,
25
- createdBySubject: { type: "user" as const, id: "u1" },
26
27
  createdAt: updatedAt,
27
28
  updatedAt,
29
+ isMutable: true,
28
30
  };
29
31
  }
30
32
 
33
+ function mockServer(partial: Partial<AgentChatServer>): AgentChatServer {
34
+ return partial as AgentChatServer;
35
+ }
36
+
31
37
  describe("createTrueFoundryOwnedSessionsThreadListAdapter", () => {
32
38
  it("lists owned sessions (named + draft) with pagination cursor", async () => {
33
- const listOwnedSessions = vi.fn().mockResolvedValue({
39
+ const listSessions = vi.fn().mockResolvedValue({
34
40
  data: [
35
41
  mockNamedSession("s1", "Named chat", "2026-06-30T12:00:00.000Z"),
36
42
  mockDraftSession("d1", "Draft chat", "2026-06-30T11:00:00.000Z"),
37
43
  ],
38
- response: {
39
- pagination: { nextPageToken: "page-2", limit: 20 },
40
- },
44
+ nextPageToken: "page-2",
41
45
  });
42
- const privateClient = {
43
- listOwnedSessions,
44
- getDraftSession: vi.fn(),
45
- } as unknown as PrivateAgentSessionClient;
46
+ const server = mockServer({ listSessions, getSession: vi.fn() });
46
47
 
47
- const adapter = createTrueFoundryOwnedSessionsThreadListAdapter({ privateClient });
48
+ const adapter = createTrueFoundryOwnedSessionsThreadListAdapter({ server });
48
49
  const result = await adapter.list();
49
50
 
50
- expect(listOwnedSessions).toHaveBeenCalledWith(
51
+ expect(listSessions).toHaveBeenCalledWith(
51
52
  expect.objectContaining({
52
53
  limit: 20,
53
54
  pageToken: undefined,
@@ -72,28 +73,24 @@ describe("createTrueFoundryOwnedSessionsThreadListAdapter", () => {
72
73
  });
73
74
 
74
75
  it("falls back to model name for untitled drafts", async () => {
75
- const listOwnedSessions = vi.fn().mockResolvedValue({
76
+ const listSessions = vi.fn().mockResolvedValue({
76
77
  data: [mockDraftSession("d1", undefined, "2026-06-30T11:00:00.000Z")],
77
- response: { pagination: { limit: 20 } },
78
78
  });
79
- const privateClient = {
80
- listOwnedSessions,
81
- getDraftSession: vi.fn(),
82
- } as unknown as PrivateAgentSessionClient;
79
+ const server = mockServer({ listSessions, getSession: vi.fn() });
83
80
 
84
- const adapter = createTrueFoundryOwnedSessionsThreadListAdapter({ privateClient });
81
+ const adapter = createTrueFoundryOwnedSessionsThreadListAdapter({ server });
85
82
  const result = await adapter.list();
86
83
 
87
84
  expect(result.threads[0]?.title).toBe("anthropic/claude-sonnet-4-6");
88
85
  });
89
86
 
90
87
  it("throws on initialize because the adapter is read-only", async () => {
91
- const privateClient = {
92
- listOwnedSessions: vi.fn(),
93
- getDraftSession: vi.fn(),
94
- } as unknown as PrivateAgentSessionClient;
88
+ const server = mockServer({
89
+ listSessions: vi.fn(),
90
+ getSession: vi.fn(),
91
+ });
95
92
 
96
- const adapter = createTrueFoundryOwnedSessionsThreadListAdapter({ privateClient });
93
+ const adapter = createTrueFoundryOwnedSessionsThreadListAdapter({ server });
97
94
 
98
95
  await expect(adapter.initialize("local")).rejects.toThrow(/read-only/);
99
96
  });
@@ -1,34 +1,33 @@
1
1
  import type { RemoteThreadListAdapter } from "@assistant-ui/core";
2
- import type { AgentSession } from "truefoundry-gateway-sdk/agents";
3
- import type {
4
- AgentDraftSession,
5
- PrivateAgentSessionClient,
6
- } from "truefoundry-gateway-sdk/agents/private";
7
2
 
3
+ import type { AgentChatServer, Session } from "./server/types.js";
8
4
  import { draftSessionTitle } from "./private/agentSpec.js";
9
5
  import { sessionListStartTimestamp } from "./sessionListStartTimestamp.js";
10
6
 
11
7
  const THREAD_LIST_PAGE_SIZE = 20;
12
8
 
13
- function ownedSessionTitle(session: AgentSession | AgentDraftSession): string {
14
- if (session.type === "session/draft") {
15
- return draftSessionTitle(session);
9
+ function ownedSessionTitle(session: Session): string {
10
+ if (session.isMutable && session.agentSpec != null) {
11
+ return draftSessionTitle({
12
+ title: session.title,
13
+ agentSpec: session.agentSpec,
14
+ });
16
15
  }
17
- return session.title ?? session.agentName;
16
+ return session.title ?? session.agentName ?? session.id;
18
17
  }
19
18
 
20
19
  /**
21
- * Read-only thread-list adapter backed by `PrivateAgentSessionClient.listOwnedSessions`.
22
- * Returns every session the caller owns (named + draft), newest first.
20
+ * Read-only thread-list adapter backed by `AgentChatServer.listSessions`.
21
+ * Hosts that previously used listOwnedSessions should filter in their server impl.
23
22
  */
24
23
  export function createTrueFoundryOwnedSessionsThreadListAdapter(options: {
25
- privateClient: PrivateAgentSessionClient;
24
+ server: AgentChatServer;
26
25
  }): RemoteThreadListAdapter {
27
- const { privateClient } = options;
26
+ const { server } = options;
28
27
 
29
28
  return {
30
29
  async list({ after } = {}) {
31
- const page = await privateClient.listOwnedSessions({
30
+ const page = await server.listSessions({
32
31
  limit: THREAD_LIST_PAGE_SIZE,
33
32
  pageToken: after,
34
33
  startTimestamp: sessionListStartTimestamp(),
@@ -41,7 +40,7 @@ export function createTrueFoundryOwnedSessionsThreadListAdapter(options: {
41
40
  }));
42
41
  return {
43
42
  threads,
44
- nextCursor: page.response.pagination.nextPageToken ?? undefined,
43
+ nextCursor: page.nextPageToken ?? undefined,
45
44
  };
46
45
  },
47
46
 
@@ -52,16 +51,12 @@ export function createTrueFoundryOwnedSessionsThreadListAdapter(options: {
52
51
  },
53
52
 
54
53
  async fetch(remoteId) {
55
- // PrivateAgentSessionClient only exposes get for drafts. Named sessions
56
- // surface through listOwnedSessions; fetch falls back to draft get.
57
- const draft = await privateClient.getDraftSession({
58
- draftSessionId: remoteId,
59
- });
54
+ const session = await server.getSession({ sessionId: remoteId });
60
55
  return {
61
56
  status: "regular" as const,
62
- remoteId: draft.id,
63
- title: draftSessionTitle(draft),
64
- lastMessageAt: new Date(draft.updatedAt),
57
+ remoteId: session.id,
58
+ title: ownedSessionTitle(session),
59
+ lastMessageAt: new Date(session.updatedAt),
65
60
  };
66
61
  },
67
62
 
@@ -1,32 +1,30 @@
1
1
  import { describe, expect, it, vi } from "vitest";
2
2
 
3
- import type { AgentSessionClient } from "truefoundry-gateway-sdk/agents";
3
+ import type { AgentChatServer, Session } from "./server/index.js";
4
4
 
5
5
  import { createTrueFoundryThreadListAdapter } from "./truefoundryThreadListAdapter.js";
6
6
 
7
- function mockSession(id: string, title: string, updatedAt: string) {
7
+ function mockSession(id: string, title: string, updatedAt: string): Session {
8
8
  return {
9
9
  id,
10
10
  title,
11
11
  updatedAt,
12
+ createdAt: updatedAt,
13
+ isMutable: false,
12
14
  };
13
15
  }
14
16
 
15
- function mockListSessionsPage(
16
- sessions: ReturnType<typeof mockSession>[],
17
- nextPageToken?: string,
18
- ) {
17
+ function mockListSessionsPage(sessions: Session[], nextPageToken?: string) {
19
18
  return {
20
19
  data: sessions,
21
- response: {
22
- pagination: {
23
- nextPageToken,
24
- limit: 20,
25
- },
26
- },
20
+ ...(nextPageToken != null ? { nextPageToken } : {}),
27
21
  };
28
22
  }
29
23
 
24
+ function mockServer(partial: Partial<AgentChatServer>): AgentChatServer {
25
+ return partial as AgentChatServer;
26
+ }
27
+
30
28
  describe("createTrueFoundryThreadListAdapter", () => {
31
29
  it("lists the first page with limit and returns nextCursor", async () => {
32
30
  const listSessions = vi.fn().mockResolvedValue(
@@ -35,9 +33,9 @@ describe("createTrueFoundryThreadListAdapter", () => {
35
33
  "page-2",
36
34
  ),
37
35
  );
38
- const client = { listSessions } as unknown as AgentSessionClient;
36
+ const server = mockServer({ listSessions });
39
37
  const adapter = createTrueFoundryThreadListAdapter({
40
- client,
38
+ server,
41
39
  agentName: "my-agent",
42
40
  });
43
41
 
@@ -68,9 +66,9 @@ describe("createTrueFoundryThreadListAdapter", () => {
68
66
  [mockSession("s2", "Second", "2026-06-29T10:00:00.000Z")],
69
67
  ),
70
68
  );
71
- const client = { listSessions } as unknown as AgentSessionClient;
69
+ const server = mockServer({ listSessions });
72
70
  const adapter = createTrueFoundryThreadListAdapter({
73
- client,
71
+ server,
74
72
  agentName: "my-agent",
75
73
  });
76
74
 
@@ -90,9 +88,9 @@ describe("createTrueFoundryThreadListAdapter", () => {
90
88
  const listSessions = vi.fn().mockResolvedValue(
91
89
  mockListSessionsPage([mockSession("s1", "Only", "2026-06-30T10:00:00.000Z")]),
92
90
  );
93
- const client = { listSessions } as unknown as AgentSessionClient;
91
+ const server = mockServer({ listSessions });
94
92
  const adapter = createTrueFoundryThreadListAdapter({
95
- client,
93
+ server,
96
94
  agentName: "my-agent",
97
95
  });
98
96
 
@@ -1,20 +1,20 @@
1
1
  import type { RemoteThreadListAdapter } from "@assistant-ui/core";
2
- import type { AgentSessionClient } from "truefoundry-gateway-sdk/agents";
3
2
 
3
+ import type { AgentChatServer } from "./server/types.js";
4
4
  import { getSession } from "./sessions.js";
5
5
  import { sessionListStartTimestamp } from "./sessionListStartTimestamp.js";
6
6
 
7
7
  const THREAD_LIST_PAGE_SIZE = 20;
8
8
 
9
9
  export function createTrueFoundryThreadListAdapter(options: {
10
- client: AgentSessionClient;
10
+ server: AgentChatServer;
11
11
  agentName: string;
12
12
  }): RemoteThreadListAdapter {
13
- const { client, agentName } = options;
13
+ const { server, agentName } = options;
14
14
 
15
15
  return {
16
16
  async list({ after } = {}) {
17
- const page = await client.listSessions({
17
+ const page = await server.listSessions({
18
18
  agentName,
19
19
  limit: THREAD_LIST_PAGE_SIZE,
20
20
  pageToken: after,
@@ -28,17 +28,17 @@ export function createTrueFoundryThreadListAdapter(options: {
28
28
  }));
29
29
  return {
30
30
  threads,
31
- nextCursor: page.response.pagination.nextPageToken ?? undefined,
31
+ nextCursor: page.nextPageToken ?? undefined,
32
32
  };
33
33
  },
34
34
 
35
35
  async initialize(_threadId: string) {
36
- const session = await client.createSession({ agentName });
36
+ const session = await server.createSession({ agentName });
37
37
  return { remoteId: session.id, externalId: undefined };
38
38
  },
39
39
 
40
40
  async fetch(remoteId) {
41
- const session = await getSession(client, remoteId);
41
+ const session = await getSession(server, remoteId);
42
42
  return {
43
43
  status: "regular" as const,
44
44
  remoteId: session.id,
@@ -1,4 +1,4 @@
1
- import type { Turn } from "truefoundry-gateway-sdk/agents";
1
+ import type { Turn } from "./server/index.js";
2
2
 
3
3
  import {
4
4
  buildMcpAuthTextParts,
package/src/types.ts CHANGED
@@ -6,10 +6,9 @@ import type {
6
6
  RealtimeVoiceAdapter,
7
7
  SpeechSynthesisAdapter,
8
8
  } from "@assistant-ui/core";
9
- import type { AgentSessionClient } from "truefoundry-gateway-sdk/agents";
10
- import type { PrivateAgentSessionClient } from "truefoundry-gateway-sdk/agents/private";
11
9
 
12
10
  import type { AgentSpec } from "./private/agentSpec.js";
11
+ import type { AgentChatServer } from "./server/types.js";
13
12
 
14
13
  export type NamedAgentConfig = {
15
14
  mode: "named";
@@ -25,7 +24,7 @@ export type DraftAgentConfig = {
25
24
  export type TrueFoundryAgentConfig = NamedAgentConfig | DraftAgentConfig;
26
25
 
27
26
  type TrueFoundryAgentRuntimeBaseOptions = ExternalStoreSharedOptions & {
28
- client: AgentSessionClient;
27
+ server: AgentChatServer;
29
28
  initialSessionId?: string | undefined;
30
29
  threadId?: string | undefined;
31
30
  onThreadIdChange?: ((threadId: string | undefined) => void) | undefined;
@@ -47,16 +46,10 @@ export type UseTrueFoundryAgentRuntimeOptions = TrueFoundryAgentRuntimeBaseOptio
47
46
  agent?: TrueFoundryAgentConfig | undefined;
48
47
  /** Legacy named-agent shorthand. Prefer `agent: { mode: "named", agentName }`. */
49
48
  agentName?: string | undefined;
50
- /**
51
- * Required when `agent.mode === "draft"`. Also used for sandbox file downloads
52
- * (named or draft).
53
- */
54
- privateClient?: PrivateAgentSessionClient | undefined;
55
49
  };
56
50
 
57
51
  export type ResolvedTrueFoundryAgentRuntimeOptions = TrueFoundryAgentRuntimeBaseOptions & {
58
52
  agent: TrueFoundryAgentConfig;
59
- privateClient?: PrivateAgentSessionClient | undefined;
60
53
  };
61
54
 
62
55
  export function resolveTrueFoundryAgentConfig(
@@ -81,15 +74,8 @@ export function resolveTrueFoundryAgentRuntimeOptions(
81
74
  ): ResolvedTrueFoundryAgentRuntimeOptions {
82
75
  const agent = resolveTrueFoundryAgentConfig(options);
83
76
 
84
- if (agent.mode === "draft" && options.privateClient == null) {
85
- throw new Error(
86
- "Draft agent mode requires a `privateClient` PrivateAgentSessionClient.",
87
- );
88
- }
89
-
90
77
  return {
91
78
  ...options,
92
79
  agent,
93
- privateClient: options.privateClient,
94
80
  };
95
81
  }