@truefoundry/assistant-ui-runtime 0.1.5 → 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 (58) hide show
  1. package/README.md +374 -190
  2. package/dist/index.d.ts +32 -29
  3. package/dist/index.js +330 -236
  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 +10 -2
  10. package/src/askUserQuestion.ts +3 -3
  11. package/src/buildEditedUserMessageContent.test.ts +2 -2
  12. package/src/collectPending.ts +1 -1
  13. package/src/convertTurnMessages.test.ts +141 -196
  14. package/src/convertTurnMessages.ts +130 -76
  15. package/src/createSubAgent.ts +1 -1
  16. package/src/draftAgentConfig.test.ts +26 -29
  17. package/src/extractTurnUserText.ts +1 -1
  18. package/src/foldPeerThreads.test.ts +1 -1
  19. package/src/foldPeerThreads.ts +3 -2
  20. package/src/index.ts +39 -4
  21. package/src/listPages.ts +21 -0
  22. package/src/loadSessionSnapshot.test.ts +9 -8
  23. package/src/loadSessionSnapshot.ts +9 -14
  24. package/src/mcpAuth.ts +6 -3
  25. package/src/messageCustomMetadata.ts +1 -1
  26. package/src/modelMessageContent.ts +1 -1
  27. package/src/modelMessageImageContent.test.ts +1 -1
  28. package/src/modelMessageImageContent.ts +7 -6
  29. package/src/plugins/truefoundry-agent-server-adapter/index.ts +285 -0
  30. package/src/private/agentSpec.ts +8 -3
  31. package/src/private/draftSessionBridge.ts +14 -13
  32. package/src/private/truefoundryDraftThreadListAdapter.test.ts +44 -49
  33. package/src/private/truefoundryDraftThreadListAdapter.ts +22 -16
  34. package/src/requiredActionInputs.ts +1 -1
  35. package/src/requiredActionsFromActiveUpdate.test.ts +1 -1
  36. package/src/server/eventUtils.ts +120 -0
  37. package/src/server/events.ts +246 -0
  38. package/src/server/index.ts +66 -0
  39. package/src/server/types.ts +313 -0
  40. package/src/sessionSnapshot.ts +1 -1
  41. package/src/sessions.ts +5 -21
  42. package/src/streamTurn.test.ts +172 -155
  43. package/src/streamTurn.ts +51 -48
  44. package/src/toolApproval.ts +4 -4
  45. package/src/toolResponse.ts +4 -4
  46. package/src/truefoundryExtras.ts +1 -1
  47. package/src/truefoundryOwnedSessionsThreadListAdapter.test.ts +26 -29
  48. package/src/truefoundryOwnedSessionsThreadListAdapter.ts +18 -23
  49. package/src/truefoundryThreadListAdapter.test.ts +16 -18
  50. package/src/truefoundryThreadListAdapter.ts +7 -7
  51. package/src/turnEventHelpers.ts +1 -1
  52. package/src/types.ts +2 -16
  53. package/src/useTrueFoundryAgentMessages.test.tsx +38 -70
  54. package/src/useTrueFoundryAgentMessages.ts +32 -44
  55. package/src/useTrueFoundryAgentRuntime.ts +11 -28
  56. package/src/private/bindDraftAgentSession.test.ts +0 -54
  57. package/src/private/bindDraftAgentSession.ts +0 -28
  58. package/src/private/getGatewayFromPrivateClient.ts +0 -13
@@ -7,7 +7,6 @@ import type {
7
7
  ThreadUserMessagePart,
8
8
  } from "@assistant-ui/core";
9
9
  import type {
10
- AgentSession,
11
10
  McpAuthRequiredEvent,
12
11
  Turn,
13
12
  TurnCreatedEvent,
@@ -15,9 +14,11 @@ import type {
15
14
  TurnEvent,
16
15
  TurnInputItem,
17
16
  TurnStreamData,
18
- } from "truefoundry-gateway-sdk/agents";
17
+ } from "./server/index.js";
18
+ import type { AgentChatServer } from "./server/types.js";
19
19
 
20
20
  import { ROOT_THREAD_ID } from "./constants.js";
21
+ import { drainListPages } from "./listPages.js";
21
22
  import { extractTurnUserText } from "./extractTurnUserText.js";
22
23
  import {
23
24
  buildRootAssistantContent,
@@ -260,22 +261,18 @@ function oldestCompleteTurnGroupState(
260
261
  }
261
262
 
262
263
  async function fetchSessionEventsPage(
263
- session: AgentSession,
264
+ server: AgentChatServer,
265
+ sessionId: string,
264
266
  options?: FetchSessionEventsOptions & { pageToken?: string },
265
267
  ): Promise<SessionEventsPageResult> {
266
- const page = await session.listEvents({
268
+ const page = await server.listEvents({
269
+ sessionId,
267
270
  limit: SESSION_EVENTS_PAGE_SIZE,
268
271
  ...(options?.lastTurnId != null ? { lastTurnId: options.lastTurnId } : {}),
269
272
  ...(options?.pageToken != null ? { pageToken: options.pageToken } : {}),
270
273
  });
271
- const response = page.response as {
272
- pagination?: { nextPageToken?: string };
273
- };
274
- const olderPageToken = response.pagination?.nextPageToken;
275
- const hasOlder =
276
- typeof page.hasNextPage === "function"
277
- ? page.hasNextPage()
278
- : olderPageToken != null && olderPageToken !== "";
274
+ const olderPageToken = page.nextPageToken;
275
+ const hasOlder = olderPageToken != null && olderPageToken !== "";
279
276
  return {
280
277
  itemsNewestFirst: page.data as GatewaySessionEventItem[],
281
278
  ...(olderPageToken != null && olderPageToken !== ""
@@ -290,7 +287,8 @@ async function fetchSessionEventsPage(
290
287
  * turn group (or history is exhausted).
291
288
  */
292
289
  async function fetchSessionEventsWindow(
293
- session: AgentSession,
290
+ server: AgentChatServer,
291
+ sessionId: string,
294
292
  options?: FetchSessionEventsOptions & { pageToken?: string },
295
293
  ): Promise<{
296
294
  itemsAsc: GatewaySessionEventItem[];
@@ -303,7 +301,7 @@ async function fetchSessionEventsWindow(
303
301
  let hasOlder = false;
304
302
 
305
303
  for (let pageCount = 0; pageCount < MAX_HISTORY_BOUNDARY_PAGES; pageCount++) {
306
- const page = await fetchSessionEventsPage(session, {
304
+ const page = await fetchSessionEventsPage(server, sessionId, {
307
305
  ...options,
308
306
  ...(pageToken != null ? { pageToken } : {}),
309
307
  });
@@ -336,25 +334,27 @@ async function fetchSessionEventsWindow(
336
334
  }
337
335
 
338
336
  /**
339
- * Fetches session-level events via `session.listEvents()`. The API returns pages
337
+ * Fetches session-level events via `server.listEvents()`. The API returns pages
340
338
  * in desc order (newest first); the collected array is reversed before returning
341
339
  * so callers receive events in chronological (asc) order.
342
340
  *
343
341
  * Used by rewind/edit paths that need the full ancestor window.
344
342
  */
345
343
  async function fetchAllSessionEvents(
346
- session: AgentSession,
344
+ server: AgentChatServer,
345
+ sessionId: string,
347
346
  options?: FetchSessionEventsOptions,
348
347
  ): Promise<GatewaySessionEventItem[]> {
349
- const items: GatewaySessionEventItem[] = [];
350
- for await (const item of await session.listEvents({
351
- limit: SESSION_EVENTS_PAGE_SIZE,
352
- ...(options?.lastTurnId != null ? { lastTurnId: options.lastTurnId } : {}),
353
- })) {
354
- items.push(item as GatewaySessionEventItem);
355
- }
348
+ const items = await drainListPages((pageToken) =>
349
+ server.listEvents({
350
+ sessionId,
351
+ limit: SESSION_EVENTS_PAGE_SIZE,
352
+ ...(options?.lastTurnId != null ? { lastTurnId: options.lastTurnId } : {}),
353
+ ...(pageToken != null ? { pageToken } : {}),
354
+ }),
355
+ );
356
356
  items.reverse();
357
- return items;
357
+ return items as GatewaySessionEventItem[];
358
358
  }
359
359
 
360
360
  function cloneThreadBucket(bucket: ThreadBucket): ThreadBucket {
@@ -545,16 +545,17 @@ function attachRunningTurn(
545
545
  * update the UI progressively while the processing loop runs.
546
546
  */
547
547
  export async function buildSnapshotFromSessionEvents(
548
- session: AgentSession,
548
+ server: AgentChatServer,
549
+ sessionId: string,
549
550
  onProgress?: (snap: SessionSnapshot) => void,
550
551
  ): Promise<SessionSnapshot> {
551
552
  // Detect a running turn with a single listTurns page — do not drain pagination.
552
- const turnsPage = await session.listTurns({ limit: 1 });
553
+ const turnsPage = await server.listTurns({ sessionId, limit: 1 });
553
554
  const newestTurn = turnsPage.data[0] as Turn | undefined;
554
555
  const runningTurn =
555
556
  newestTurn?.state?.status === "running" ? newestTurn : undefined;
556
557
 
557
- const window = await fetchSessionEventsWindow(session);
558
+ const window = await fetchSessionEventsWindow(server, sessionId);
558
559
  const historyPagination: SessionHistoryPagination = {
559
560
  hasOlder: window.hasOlder,
560
561
  ...(window.olderPageToken != null
@@ -578,7 +579,8 @@ export async function buildSnapshotFromSessionEvents(
578
579
  * without tearing down live stream / pending UI state.
579
580
  */
580
581
  export async function prependOlderSessionHistory(
581
- session: AgentSession,
582
+ server: AgentChatServer,
583
+ sessionId: string,
582
584
  snapshot: SessionSnapshot,
583
585
  ): Promise<SessionSnapshot> {
584
586
  const pagination = snapshot.historyPagination;
@@ -586,7 +588,7 @@ export async function prependOlderSessionHistory(
586
588
  return snapshot;
587
589
  }
588
590
 
589
- const window = await fetchSessionEventsWindow(session, {
591
+ const window = await fetchSessionEventsWindow(server, sessionId, {
590
592
  pageToken: pagination.olderPageToken,
591
593
  });
592
594
  if (window.itemsAsc.length === 0) {
@@ -782,28 +784,46 @@ function buildAssistantMessage(
782
784
  }
783
785
 
784
786
  async function ingestTurnEventsIntoFold(
787
+ server: AgentChatServer,
788
+ sessionId: string,
789
+ turnId: string,
785
790
  foldState: PeerThreadFoldState,
786
- turn: Pick<Turn, "listEvents">,
787
791
  ): Promise<void> {
788
- for await (const event of await turn.listEvents({
789
- order: "asc",
790
- limit: TURN_EVENTS_PAGE_SIZE,
791
- })) {
792
- ingestTurnEvent(foldState, event);
792
+ if (server.listTurnEvents == null) {
793
+ return;
794
+ }
795
+ const events = await drainListPages((pageToken) =>
796
+ server.listTurnEvents!({
797
+ sessionId,
798
+ turnId,
799
+ order: "asc",
800
+ limit: TURN_EVENTS_PAGE_SIZE,
801
+ ...(pageToken != null ? { pageToken } : {}),
802
+ }),
803
+ );
804
+ for (const event of events) {
805
+ ingestTurnEvent(foldState, event as TurnEvent);
793
806
  }
794
807
  }
795
808
 
796
809
  async function fetchTurnEvents(
797
- turn: Pick<Turn, "listEvents">,
810
+ server: AgentChatServer,
811
+ sessionId: string,
812
+ turnId: string,
798
813
  ): Promise<TurnEvent[]> {
799
- const events: TurnEvent[] = [];
800
- for await (const event of await turn.listEvents({
801
- order: "asc",
802
- limit: TURN_EVENTS_PAGE_SIZE,
803
- })) {
804
- events.push(event);
814
+ if (server.listTurnEvents == null) {
815
+ return [];
805
816
  }
806
- return events;
817
+ const events = await drainListPages((pageToken) =>
818
+ server.listTurnEvents!({
819
+ sessionId,
820
+ turnId,
821
+ order: "asc",
822
+ limit: TURN_EVENTS_PAGE_SIZE,
823
+ ...(pageToken != null ? { pageToken } : {}),
824
+ }),
825
+ );
826
+ return events as TurnEvent[];
807
827
  }
808
828
 
809
829
  function ingestCollectedEventsIntoFold(
@@ -816,6 +836,8 @@ function ingestCollectedEventsIntoFold(
816
836
  }
817
837
 
818
838
  async function fetchAllTurnEventsWithConcurrency(
839
+ server: AgentChatServer,
840
+ sessionId: string,
819
841
  turns: Turn[],
820
842
  concurrency: number,
821
843
  ): Promise<TurnEvent[][]> {
@@ -824,10 +846,12 @@ async function fetchAllTurnEventsWithConcurrency(
824
846
  for (let i = 0; i < turns.length; i++) {
825
847
  const idx = i;
826
848
  const turn = turns[idx]!;
827
- const p: Promise<void> = fetchTurnEvents(turn).then((events) => {
828
- results[idx] = events;
829
- pool.delete(p);
830
- });
849
+ const p: Promise<void> = fetchTurnEvents(server, sessionId, turn.id).then(
850
+ (events) => {
851
+ results[idx] = events;
852
+ pool.delete(p);
853
+ },
854
+ );
831
855
  pool.add(p);
832
856
  if (pool.size >= concurrency) await Promise.race(pool);
833
857
  }
@@ -1209,19 +1233,25 @@ function ingestTurnsIntoSnapshot(
1209
1233
  }
1210
1234
 
1211
1235
  export async function buildSnapshotFromSession(
1212
- session: AgentSession,
1236
+ server: AgentChatServer,
1237
+ sessionId: string,
1213
1238
  concurrency: number = DEFAULT_LIST_EVENTS_CONCURRENCY,
1214
1239
  ): Promise<SessionSnapshot> {
1215
1240
  // Completed history comes from session-level listEvents. The session API
1216
- // excludes the running turn — hydrate that turn via turn.listEvents so
1241
+ // excludes the running turn — hydrate that turn via listTurnEvents so
1217
1242
  // convertTurnsToThreadMessages still surfaces in-flight content.
1218
- const snapshot = await buildSnapshotFromSessionEvents(session);
1243
+ const snapshot = await buildSnapshotFromSessionEvents(server, sessionId);
1219
1244
  if (snapshot.runningTurn == null) {
1220
1245
  return snapshot;
1221
1246
  }
1222
1247
 
1223
1248
  const turn = snapshot.runningTurn;
1224
- const eventArrays = await fetchAllTurnEventsWithConcurrency([turn], concurrency);
1249
+ const eventArrays = await fetchAllTurnEventsWithConcurrency(
1250
+ server,
1251
+ sessionId,
1252
+ [turn],
1253
+ concurrency,
1254
+ );
1225
1255
  ingestTurnsIntoSnapshot(snapshot, [turn], eventArrays);
1226
1256
 
1227
1257
  // Hydrated in-flight content lives in `turns` / `fold` now — drop the
@@ -1236,23 +1266,31 @@ export async function buildSnapshotFromSession(
1236
1266
 
1237
1267
  /** Rebuilds session state from turns strictly before `beforeTurnId` (excludes that turn). */
1238
1268
  export async function buildSnapshotBeforeTurn(
1239
- session: AgentSession,
1269
+ server: AgentChatServer,
1270
+ sessionId: string,
1240
1271
  beforeTurnId: string,
1241
1272
  concurrency: number = DEFAULT_LIST_EVENTS_CONCURRENCY,
1242
1273
  ): Promise<SessionSnapshot> {
1243
- const turns = await listSessionTurnsOrdered(session);
1274
+ const turns = await listSessionTurnsOrdered(server, sessionId);
1244
1275
 
1245
1276
  const beforeIndex = turns.findIndex((turn) => turn.id === beforeTurnId);
1246
1277
  if (beforeIndex === -1) {
1247
1278
  throw new Error(`Turn ${beforeTurnId} not found in session`);
1248
1279
  }
1249
1280
 
1250
- return buildSnapshotBeforeTurnIndex(session, beforeIndex, concurrency, turns);
1281
+ return buildSnapshotBeforeTurnIndex(
1282
+ server,
1283
+ sessionId,
1284
+ beforeIndex,
1285
+ concurrency,
1286
+ turns,
1287
+ );
1251
1288
  }
1252
1289
 
1253
- /** Rebuilds session state from the first `turnIndex` gateway turns (excludes that turn). */
1290
+ /** Rebuilds session state from the first `turnIndex` turns (excludes that turn). */
1254
1291
  export async function buildSnapshotBeforeTurnIndex(
1255
- session: AgentSession,
1292
+ server: AgentChatServer,
1293
+ sessionId: string,
1256
1294
  turnIndex: number,
1257
1295
  _concurrency: number = DEFAULT_LIST_EVENTS_CONCURRENCY,
1258
1296
  orderedTurns?: Turn[],
@@ -1261,7 +1299,7 @@ export async function buildSnapshotBeforeTurnIndex(
1261
1299
  return createEmptySessionSnapshot();
1262
1300
  }
1263
1301
 
1264
- const turns = orderedTurns ?? (await listSessionTurnsOrdered(session));
1302
+ const turns = orderedTurns ?? (await listSessionTurnsOrdered(server, sessionId));
1265
1303
  const turnsToInclude = turns.slice(0, turnIndex);
1266
1304
  const lastTurnId = turnsToInclude.at(-1)?.id;
1267
1305
  if (lastTurnId == null) {
@@ -1270,61 +1308,71 @@ export async function buildSnapshotBeforeTurnIndex(
1270
1308
 
1271
1309
  // Anchor the session events window at the newest included turn so the
1272
1310
  // ancestor chain matches `[turns[0], …, turns[turnIndex - 1]]`.
1273
- const items = await fetchAllSessionEvents(session, { lastTurnId });
1311
+ const items = await fetchAllSessionEvents(server, sessionId, { lastTurnId });
1274
1312
  const snapshot = createEmptySessionSnapshot();
1275
1313
  ingestSessionEventsIntoSnapshot(snapshot, items);
1276
1314
  return snapshot;
1277
1315
  }
1278
1316
 
1279
- /** Gateway turn id to branch from when resubmitting at `turnIndex` (`"none"` for first turn). */
1317
+ /** Turn id to branch from when resubmitting at `turnIndex` (`"none"` for first turn). */
1280
1318
  export async function resolveGatewayBranchPreviousTurnId(
1281
- session: AgentSession,
1319
+ server: AgentChatServer,
1320
+ sessionId: string,
1282
1321
  turnIndex: number,
1283
1322
  orderedTurns?: Turn[],
1284
1323
  ): Promise<string> {
1285
1324
  if (turnIndex <= 0) {
1286
1325
  return "none";
1287
1326
  }
1288
- const turns = orderedTurns ?? (await listSessionTurnsOrdered(session));
1327
+ const turns = orderedTurns ?? (await listSessionTurnsOrdered(server, sessionId));
1289
1328
  return turns[turnIndex - 1]?.id ?? "none";
1290
1329
  }
1291
1330
 
1292
1331
  /** Resolves `previousTurnId` by turn id so partial history windows stay correct. */
1293
1332
  export async function resolveGatewayBranchPreviousTurnIdForTurn(
1294
- session: AgentSession,
1333
+ server: AgentChatServer,
1334
+ sessionId: string,
1295
1335
  turnId: string,
1296
1336
  ): Promise<string> {
1297
- const turns = await listSessionTurnsOrdered(session);
1337
+ const turns = await listSessionTurnsOrdered(server, sessionId);
1298
1338
  const turnIndex = turns.findIndex((turn) => turn.id === turnId);
1299
- return resolveGatewayBranchPreviousTurnId(session, turnIndex, turns);
1339
+ return resolveGatewayBranchPreviousTurnId(server, sessionId, turnIndex, turns);
1300
1340
  }
1301
1341
 
1302
- async function listSessionTurnsOrdered(session: AgentSession): Promise<Turn[]> {
1303
- const turns: Turn[] = [];
1304
- for await (const turn of await session.listTurns()) {
1305
- turns.push(turn);
1306
- }
1342
+ async function listSessionTurnsOrdered(
1343
+ server: AgentChatServer,
1344
+ sessionId: string,
1345
+ ): Promise<Turn[]> {
1346
+ const turns = await drainListPages((pageToken) =>
1347
+ server.listTurns({
1348
+ sessionId,
1349
+ ...(pageToken != null ? { pageToken } : {}),
1350
+ }),
1351
+ );
1307
1352
  turns.reverse();
1308
1353
  return turns;
1309
1354
  }
1310
1355
 
1311
1356
  export async function buildTurnAssistantContent(
1312
- turn: Pick<Turn, "listEvents" | "state">,
1357
+ server: AgentChatServer,
1358
+ sessionId: string,
1359
+ turn: Pick<Turn, "id" | "state">,
1313
1360
  foldState?: PeerThreadFoldState,
1314
1361
  ): Promise<AssistantContentPart[]> {
1315
1362
  const state = foldState ?? new PeerThreadFoldState();
1316
1363
  const beforeCount =
1317
1364
  state.threads.get(ROOT_THREAD_ID)?.modelMessageIds.length ?? 0;
1318
- await ingestTurnEventsIntoFold(state, turn);
1365
+ await ingestTurnEventsIntoFold(server, sessionId, turn.id, state);
1319
1366
  const afterIds = state.threads.get(ROOT_THREAD_ID)?.modelMessageIds ?? [];
1320
1367
  const rootModelMessageIds = afterIds.slice(beforeCount);
1321
1368
  return buildTurnUpdateFromFold(state, turn, rootModelMessageIds).content;
1322
1369
  }
1323
1370
 
1324
1371
  export async function convertTurnsToThreadMessages(
1325
- session: AgentSession,
1372
+ server: AgentChatServer,
1373
+ sessionId: string,
1326
1374
  ): Promise<ConvertTurnsResult> {
1327
- const snapshot = await buildSnapshotFromSession(session);
1375
+ const snapshot = await buildSnapshotFromSession(server, sessionId);
1328
1376
  const messages = projectSessionMessages(snapshot);
1329
1377
 
1330
1378
  return {
@@ -1456,14 +1504,14 @@ export function parseTurnIdFromMessageId(messageId: string): string {
1456
1504
  return messageId.replace(/-user$/, "");
1457
1505
  }
1458
1506
 
1459
- /** Parent turn id for branching before `turnId`; `"none"` when editing the first turn. */
1507
+ /** Parent turn id for branching before `turnId`; `null` when editing the first turn. */
1460
1508
  export function resolveBranchPreviousTurnId(
1461
1509
  turns: readonly SessionTurnRecord[],
1462
1510
  turnId: string,
1463
- ): string {
1511
+ ): string | null {
1464
1512
  const turnIndex = turns.findIndex((turn) => turn.id === turnId);
1465
1513
  if (turnIndex <= 0) {
1466
- return "none";
1514
+ return null;
1467
1515
  }
1468
1516
  return turns[turnIndex - 1]!.id;
1469
1517
  }
@@ -1544,6 +1592,7 @@ export async function* streamTurnEvents(
1544
1592
  stream: AsyncIterable<TurnStreamData>,
1545
1593
  foldState: PeerThreadFoldState,
1546
1594
  groupRootBaseline?: readonly string[],
1595
+ onTurnIdAvailable?: (turnId: string) => void,
1547
1596
  ): AsyncGenerator<TurnStreamUpdate> {
1548
1597
  let pendingMcpAuth: McpAuthRequiredEvent | undefined;
1549
1598
  let sandboxId: string | undefined;
@@ -1572,6 +1621,11 @@ export async function* streamTurnEvents(
1572
1621
  for await (const data of stream) {
1573
1622
  const event = data.event;
1574
1623
 
1624
+ if (event.type === "turn.created") {
1625
+ onTurnIdAvailable?.(event.turnId);
1626
+ continue;
1627
+ }
1628
+
1575
1629
  if (event.type === "sandbox.created") {
1576
1630
  sandboxId = event.sandboxId;
1577
1631
  continue;
@@ -1,4 +1,4 @@
1
- import type { ToolCall } from "truefoundry-gateway-sdk/agents";
1
+ import type { ToolCall } from "./server/index.js";
2
2
 
3
3
  export function isCreateSubAgentToolCall(
4
4
  toolCall: Pick<ToolCall, "toolInfo" | "function">,
@@ -5,8 +5,7 @@ import {
5
5
  resolveTrueFoundryAgentConfig,
6
6
  resolveTrueFoundryAgentRuntimeOptions,
7
7
  } from "./types.js";
8
- import type { AgentSessionClient } from "truefoundry-gateway-sdk/agents";
9
- import type { PrivateAgentSessionClient } from "truefoundry-gateway-sdk/agents/private";
8
+ import type { AgentChatServer } from "./server/index.js";
10
9
 
11
10
  describe("resolveTrueFoundryAgentConfig", () => {
12
11
  it("supports legacy agentName", () => {
@@ -42,27 +41,25 @@ describe("resolveTrueFoundryAgentConfig", () => {
42
41
  });
43
42
 
44
43
  describe("resolveTrueFoundryAgentRuntimeOptions", () => {
45
- const client = {} as AgentSessionClient;
44
+ const server = {} as AgentChatServer;
46
45
 
47
- it("requires privateClient for draft mode", () => {
48
- expect(() =>
49
- resolveTrueFoundryAgentRuntimeOptions({
50
- client,
51
- agent: { mode: "draft", defaultAgentSpec: { model: { name: "x" } } },
52
- }),
53
- ).toThrow(/privateClient/);
54
- });
55
-
56
- it("accepts privateClient for draft mode", () => {
57
- const privateClient = {} as PrivateAgentSessionClient;
46
+ it("accepts draft mode without a private client", () => {
58
47
  const resolved = resolveTrueFoundryAgentRuntimeOptions({
59
- client,
60
- privateClient,
48
+ server,
61
49
  agent: { mode: "draft", defaultAgentSpec: { model: { name: "x" } } },
62
50
  });
63
- expect(resolved.privateClient).toBe(privateClient);
51
+ expect(resolved.server).toBe(server);
64
52
  expect(resolved.agent.mode).toBe("draft");
65
53
  });
54
+
55
+ it("resolves named mode with server", () => {
56
+ const resolved = resolveTrueFoundryAgentRuntimeOptions({
57
+ server,
58
+ agent: { mode: "named", agentName: "my-agent" },
59
+ });
60
+ expect(resolved.server).toBe(server);
61
+ expect(resolved.agent).toEqual({ mode: "named", agentName: "my-agent" });
62
+ });
66
63
  });
67
64
 
68
65
  describe("mergeAgentSpec", () => {
@@ -70,42 +67,42 @@ describe("mergeAgentSpec", () => {
70
67
  const base = {
71
68
  model: {
72
69
  name: "anthropic/claude-sonnet-4-6",
73
- params: { maxTokens: 1024, temperature: 0.5 },
70
+ params: { maxTokens: 1024, reasoningEffort: "medium" },
74
71
  },
75
72
  };
76
73
  const next = mergeAgentSpec(base, {
77
- model: { params: { temperature: 1.0 } },
74
+ model: { params: { reasoningEffort: "high" } },
78
75
  });
79
- expect(next.model.params).toEqual({ maxTokens: 1024, temperature: 1.0 });
76
+ expect(next.model.params).toEqual({ maxTokens: 1024, reasoningEffort: "high" });
80
77
  });
81
78
 
82
79
  it("replaces mcpServers array wholesale", () => {
83
80
  const base: AgentSpec = {
84
81
  model: { name: "openai/gpt-4o" },
85
- mcpServers: [{ type: "truefoundry-mcp-registry", name: "github", enableTools: ["@all"] }],
82
+ mcpServers: [{ id: "github", name: "github" }],
86
83
  };
87
84
  const next = mergeAgentSpec(base, {
88
- mcpServers: [{ type: "truefoundry-mcp-registry", name: "slack", enableTools: ["@all"] }],
85
+ mcpServers: [{ id: "slack", name: "slack" }],
89
86
  });
90
- expect(next.mcpServers).toEqual([{ type: "truefoundry-mcp-registry", name: "slack", enableTools: ["@all"] }]);
87
+ expect(next.mcpServers).toEqual([{ id: "slack", name: "slack" }]);
91
88
  });
92
89
 
93
90
  it("replaces skills array wholesale", () => {
94
- const base = {
91
+ const base: AgentSpec = {
95
92
  model: { name: "openai/gpt-4o" },
96
- skills: [{ fqn: "acme/skill-a:1", preload: false }],
93
+ skills: [{ id: "skill-a", name: "skill-a" }],
97
94
  };
98
95
  const next = mergeAgentSpec(base, {
99
- skills: [{ fqn: "acme/skill-b:2", preload: true }],
96
+ skills: [{ id: "skill-b", name: "skill-b" }],
100
97
  });
101
- expect(next.skills).toEqual([{ fqn: "acme/skill-b:2", preload: true }]);
98
+ expect(next.skills).toEqual([{ id: "skill-b", name: "skill-b" }]);
102
99
  });
103
100
 
104
101
  it("model partial update does not clear mcpServers or skills", () => {
105
102
  const base: AgentSpec = {
106
103
  model: { name: "openai/gpt-4o", params: { maxTokens: 1024 } },
107
- mcpServers: [{ type: "truefoundry-mcp-registry", name: "github", enableTools: ["@all"] }],
108
- skills: [{ fqn: "acme/skill-a:1", preload: false }],
104
+ mcpServers: [{ id: "github", name: "github" }],
105
+ skills: [{ id: "skill-a", name: "skill-a" }],
109
106
  };
110
107
  const next = mergeAgentSpec(base, {
111
108
  model: { name: "anthropic/claude-sonnet-4-6" },
@@ -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
  export function extractTurnUserText(input: Turn["input"]): string {
4
4
  const parts: string[] = [];
@@ -3,7 +3,7 @@ import type {
3
3
  ModelMessageEvent,
4
4
  ThreadCreatedEvent,
5
5
  TurnEvent,
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 {
@@ -1,10 +1,11 @@
1
1
  import type { MessageStatus, ThreadMessage } from "@assistant-ui/core";
2
- import type { ThreadCreatedEvent, ToolResponseRequiredEvent } from "truefoundry-gateway-sdk/agents";
3
2
  import {
4
3
  isEventDelta,
4
+ type ThreadCreatedEvent,
5
+ type ToolResponseRequiredEvent,
5
6
  type TurnEvent,
6
7
  type TurnStreamingEvent,
7
- } from "truefoundry-gateway-sdk/agents";
8
+ } from "./server/index.js";
8
9
 
9
10
  import { parseAskUserQuestionArgs } from "./askUserQuestion.js";
10
11
  import { isCreateSubAgentToolCall } from "./createSubAgent.js";
package/src/index.ts CHANGED
@@ -12,15 +12,18 @@ export {
12
12
  } from "./convertTurnMessages.js";
13
13
  export type { ConvertTurnsResult, UserMessageContent } from "./convertTurnMessages.js";
14
14
  export { ROOT_THREAD_ID } from "./constants.js";
15
- export type { UseTrueFoundryAgentRuntimeOptions, NamedAgentConfig, DraftAgentConfig, TrueFoundryAgentConfig } from "./types.js";
15
+ export type {
16
+ UseTrueFoundryAgentRuntimeOptions,
17
+ NamedAgentConfig,
18
+ DraftAgentConfig,
19
+ TrueFoundryAgentConfig,
20
+ } from "./types.js";
16
21
  export type { AgentSpec, AgentSpecUpdate, DraftSession } from "./private/agentSpec.js";
17
22
  export { mergeAgentSpec, draftSessionTitle } from "./private/agentSpec.js";
18
23
  export { createTrueFoundryDraftThreadListAdapter } from "./private/truefoundryDraftThreadListAdapter.js";
19
24
  export { createTrueFoundryOwnedSessionsThreadListAdapter } from "./truefoundryOwnedSessionsThreadListAdapter.js";
20
25
  export { createDraftSessionBridge } from "./private/draftSessionBridge.js";
21
26
  export type { DraftSessionBridge } from "./private/draftSessionBridge.js";
22
- export { PrivateAgentSessionClient } from "truefoundry-gateway-sdk/agents/private";
23
- export type { AgentDraftSession } from "truefoundry-gateway-sdk/agents/private";
24
27
  export {
25
28
  useTrueFoundryAgentSpec,
26
29
  useTrueFoundryUpdateAgentSpec,
@@ -35,7 +38,6 @@ export type {
35
38
  ToolApprovalMessageCustomMetadata,
36
39
  ToolResponseMessageCustomMetadata,
37
40
  } from "./messageCustomMetadata.js";
38
- export type { SandboxCreatedEvent } from "truefoundry-gateway-sdk/agents";
39
41
  export type { PendingApproval, PendingToolResponse } from "./collectPending.js";
40
42
  export type { TrueFoundryRuntimeExtras } from "./truefoundryExtras.js";
41
43
  export { trueFoundryExtras } from "./truefoundryExtras.js";
@@ -71,3 +73,36 @@ export {
71
73
  export { createTrueFoundryThreadListAdapter } from "./truefoundryThreadListAdapter.js";
72
74
  export { getSession } from "./sessions.js";
73
75
  export { trueFoundryAttachmentAdapter } from "./attachmentAdapter.js";
76
+
77
+ export type {
78
+ AgentChatServer,
79
+ AgentBuilderServer,
80
+ Session,
81
+ Turn,
82
+ TurnState,
83
+ TurnStateDone,
84
+ TurnInputItem,
85
+ AgentSpec as ServerAgentSpec,
86
+ ListResult,
87
+ CreateSessionRequest,
88
+ UpdateSessionRequest,
89
+ ListSessionsParams,
90
+ UserMessage,
91
+ UserToolApprovalEvent,
92
+ UserToolResponseEvent,
93
+ PreviousTurnIdInput,
94
+ } from "./server/index.js";
95
+ export type {
96
+ SandboxCreatedEvent,
97
+ McpAuthRequiredEvent,
98
+ ModelMessageEvent,
99
+ TurnEvent,
100
+ TurnStreamingEvent,
101
+ TurnStreamData,
102
+ SessionEventItem,
103
+ ToolCall,
104
+ ThreadCreatedEvent,
105
+ ToolApprovalRequiredEvent,
106
+ ToolResponseRequiredEvent,
107
+ } from "./server/index.js";
108
+ export { isEventDelta, mergeEventDelta } from "./server/index.js";
@@ -0,0 +1,21 @@
1
+ import type { ListResult } from "./server/types.js";
2
+
3
+ /**
4
+ * Drains all pages of a token-paginated list into a single array.
5
+ * Newest-first APIs should reverse after calling this.
6
+ */
7
+ export async function drainListPages<T>(
8
+ fetchPage: (pageToken?: string) => Promise<ListResult<T>>,
9
+ ): Promise<T[]> {
10
+ const items: T[] = [];
11
+ let pageToken: string | undefined;
12
+ for (;;) {
13
+ const page = await fetchPage(pageToken);
14
+ items.push(...page.data);
15
+ if (page.nextPageToken == null || page.nextPageToken === "") {
16
+ break;
17
+ }
18
+ pageToken = page.nextPageToken;
19
+ }
20
+ return items;
21
+ }