@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.
- package/README.md +374 -190
- package/dist/index.d.ts +32 -29
- package/dist/index.js +334 -241
- package/dist/index.js.map +1 -1
- package/dist/plugins/truefoundry-agent-server-adapter/index.d.ts +30 -0
- package/dist/plugins/truefoundry-agent-server-adapter/index.js +198 -0
- package/dist/plugins/truefoundry-agent-server-adapter/index.js.map +1 -0
- package/dist/types-VUBzoJT2.d.ts +462 -0
- package/package.json +12 -4
- package/src/askUserQuestion.ts +3 -3
- package/src/collectPending.ts +1 -1
- package/src/convertTurnMessages.test.ts +141 -196
- package/src/convertTurnMessages.ts +131 -77
- package/src/createSubAgent.ts +1 -1
- package/src/draftAgentConfig.test.ts +26 -29
- package/src/extractTurnUserText.ts +1 -1
- package/src/foldPeerThreads.test.ts +1 -1
- package/src/foldPeerThreads.ts +3 -2
- package/src/index.ts +39 -4
- package/src/listPages.ts +21 -0
- package/src/loadSessionSnapshot.test.ts +9 -8
- package/src/loadSessionSnapshot.ts +9 -14
- package/src/mcpAuth.ts +6 -3
- package/src/messageCustomMetadata.ts +1 -1
- package/src/modelMessageContent.ts +1 -1
- package/src/modelMessageImageContent.test.ts +1 -1
- package/src/modelMessageImageContent.ts +7 -6
- package/src/plugins/truefoundry-agent-server-adapter/index.ts +285 -0
- package/src/private/agentSpec.ts +8 -3
- package/src/private/draftSessionBridge.ts +14 -13
- package/src/private/truefoundryDraftThreadListAdapter.test.ts +44 -49
- package/src/private/truefoundryDraftThreadListAdapter.ts +22 -16
- package/src/requiredActionInputs.ts +1 -1
- package/src/requiredActionsFromActiveUpdate.test.ts +1 -1
- package/src/server/eventUtils.ts +120 -0
- package/src/server/events.ts +246 -0
- package/src/server/index.ts +66 -0
- package/src/server/types.ts +313 -0
- package/src/sessionSnapshot.ts +1 -1
- package/src/sessions.ts +5 -21
- package/src/streamTurn.test.ts +175 -158
- package/src/streamTurn.ts +51 -57
- package/src/toolApproval.ts +4 -4
- package/src/toolResponse.ts +4 -4
- package/src/truefoundryExtras.ts +1 -1
- package/src/truefoundryOwnedSessionsThreadListAdapter.test.ts +26 -29
- package/src/truefoundryOwnedSessionsThreadListAdapter.ts +18 -23
- package/src/truefoundryThreadListAdapter.test.ts +16 -18
- package/src/truefoundryThreadListAdapter.ts +9 -9
- package/src/turnEventHelpers.ts +1 -1
- package/src/types.ts +2 -16
- package/src/useTrueFoundryAgentMessages.test.tsx +38 -70
- package/src/useTrueFoundryAgentMessages.ts +33 -45
- package/src/useTrueFoundryAgentRuntime.ts +11 -28
- package/src/private/bindDraftAgentSession.test.ts +0 -54
- package/src/private/bindDraftAgentSession.ts +0 -28
- package/src/private/getGatewayFromPrivateClient.ts +0 -13
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
import type { ThreadMessage } from "@assistant-ui/core";
|
|
3
3
|
import { act, renderHook, waitFor } from "@testing-library/react";
|
|
4
4
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
-
import type {
|
|
6
|
-
import type { PrivateAgentSessionClient } from "truefoundry-gateway-sdk/agents/private";
|
|
5
|
+
import type { AgentChatServer, Turn } from "./server/index.js";
|
|
7
6
|
|
|
8
7
|
import { ROOT_THREAD_ID } from "./constants.js";
|
|
9
8
|
import { collectPendingToolResponses } from "./collectPending.js";
|
|
@@ -18,7 +17,6 @@ import {
|
|
|
18
17
|
replaceSessionSnapshot,
|
|
19
18
|
type SessionSnapshot,
|
|
20
19
|
} from "./sessionSnapshot.js";
|
|
21
|
-
import { getSession } from "./sessions.js";
|
|
22
20
|
import { resumeTurnStream, streamTurnContent } from "./streamTurn.js";
|
|
23
21
|
import {
|
|
24
22
|
messageHasPendingApprovals,
|
|
@@ -32,10 +30,6 @@ import {
|
|
|
32
30
|
} from "./toolResponse.js";
|
|
33
31
|
import { useTrueFoundryAgentMessages } from "./useTrueFoundryAgentMessages.js";
|
|
34
32
|
|
|
35
|
-
vi.mock("./sessions.js", () => ({
|
|
36
|
-
getSession: vi.fn(),
|
|
37
|
-
}));
|
|
38
|
-
|
|
39
33
|
vi.mock("./loadSessionSnapshot.js", () => ({
|
|
40
34
|
loadSessionSnapshot: vi.fn(),
|
|
41
35
|
}));
|
|
@@ -50,7 +44,9 @@ vi.mock("./convertTurnMessages.js", async (importOriginal) => {
|
|
|
50
44
|
return actual;
|
|
51
45
|
});
|
|
52
46
|
|
|
53
|
-
const
|
|
47
|
+
const mockServer = {
|
|
48
|
+
cancelSession: vi.fn().mockResolvedValue(undefined),
|
|
49
|
+
} as unknown as AgentChatServer;
|
|
54
50
|
|
|
55
51
|
function snapshotWithAssistantMessage(
|
|
56
52
|
message: Extract<ThreadMessage, { role: "assistant" }>,
|
|
@@ -282,7 +278,7 @@ async function* singleUpdateStream() {
|
|
|
282
278
|
describe("useTrueFoundryAgentMessages", () => {
|
|
283
279
|
beforeEach(() => {
|
|
284
280
|
vi.clearAllMocks();
|
|
285
|
-
vi.mocked(
|
|
281
|
+
vi.mocked(mockServer.cancelSession).mockResolvedValue(undefined);
|
|
286
282
|
vi.mocked(loadSessionSnapshot).mockResolvedValue(createEmptySessionSnapshot());
|
|
287
283
|
vi.mocked(streamTurnContent).mockReturnValue(singleUpdateStream());
|
|
288
284
|
vi.mocked(resumeTurnStream).mockReturnValue(singleUpdateStream());
|
|
@@ -294,12 +290,11 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
294
290
|
|
|
295
291
|
it("clears messages when sessionId is undefined", async () => {
|
|
296
292
|
const { result } = renderHook(() =>
|
|
297
|
-
useTrueFoundryAgentMessages({
|
|
293
|
+
useTrueFoundryAgentMessages({ server: mockServer, sessionId: undefined }),
|
|
298
294
|
);
|
|
299
295
|
|
|
300
296
|
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
301
297
|
expect(result.current.messages).toEqual([]);
|
|
302
|
-
expect(getSession).not.toHaveBeenCalled();
|
|
303
298
|
expect(loadSessionSnapshot).not.toHaveBeenCalled();
|
|
304
299
|
});
|
|
305
300
|
|
|
@@ -311,7 +306,7 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
311
306
|
|
|
312
307
|
const { result } = renderHook(() =>
|
|
313
308
|
useTrueFoundryAgentMessages({
|
|
314
|
-
|
|
309
|
+
server: mockServer,
|
|
315
310
|
sessionId: undefined,
|
|
316
311
|
initializeSession,
|
|
317
312
|
}),
|
|
@@ -324,7 +319,6 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
324
319
|
});
|
|
325
320
|
|
|
326
321
|
expect(initializeSession).toHaveBeenCalledOnce();
|
|
327
|
-
expect(getSession).toHaveBeenCalledWith(mockClient, "session-new");
|
|
328
322
|
expect(streamTurnContent).toHaveBeenCalled();
|
|
329
323
|
expect(loadSessionSnapshot).not.toHaveBeenCalled();
|
|
330
324
|
});
|
|
@@ -339,7 +333,7 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
339
333
|
|
|
340
334
|
const { result } = renderHook(() =>
|
|
341
335
|
useTrueFoundryAgentMessages({
|
|
342
|
-
|
|
336
|
+
server: mockServer,
|
|
343
337
|
sessionId: "session-1",
|
|
344
338
|
getTurnHeaders,
|
|
345
339
|
}),
|
|
@@ -353,7 +347,8 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
353
347
|
|
|
354
348
|
expect(getTurnHeaders).toHaveBeenCalledTimes(1);
|
|
355
349
|
expect(streamTurnContent).toHaveBeenCalledWith(
|
|
356
|
-
|
|
350
|
+
mockServer,
|
|
351
|
+
"session-1",
|
|
357
352
|
expect.any(PeerThreadFoldState),
|
|
358
353
|
{
|
|
359
354
|
userMessage: "first",
|
|
@@ -371,7 +366,8 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
371
366
|
|
|
372
367
|
expect(getTurnHeaders).toHaveBeenCalledTimes(2);
|
|
373
368
|
expect(streamTurnContent).toHaveBeenLastCalledWith(
|
|
374
|
-
|
|
369
|
+
mockServer,
|
|
370
|
+
"session-1",
|
|
375
371
|
expect.any(PeerThreadFoldState),
|
|
376
372
|
{ userMessage: "second" },
|
|
377
373
|
expect.any(AbortSignal),
|
|
@@ -385,11 +381,11 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
385
381
|
);
|
|
386
382
|
|
|
387
383
|
const { result } = renderHook(() =>
|
|
388
|
-
useTrueFoundryAgentMessages({
|
|
384
|
+
useTrueFoundryAgentMessages({ server: mockServer, sessionId: "session-1" }),
|
|
389
385
|
);
|
|
390
386
|
|
|
391
387
|
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
392
|
-
expect(loadSessionSnapshot).toHaveBeenCalledWith(
|
|
388
|
+
expect(loadSessionSnapshot).toHaveBeenCalledWith(mockServer, "session-1");
|
|
393
389
|
expect(result.current.messages).toHaveLength(1);
|
|
394
390
|
expect(result.current.messages[0]?.role).toBe("user");
|
|
395
391
|
});
|
|
@@ -412,7 +408,7 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
412
408
|
);
|
|
413
409
|
|
|
414
410
|
const { result } = renderHook(() =>
|
|
415
|
-
useTrueFoundryAgentMessages({
|
|
411
|
+
useTrueFoundryAgentMessages({ server: mockServer, sessionId: "session-1" }),
|
|
416
412
|
);
|
|
417
413
|
|
|
418
414
|
await waitFor(() => expect(result.current.isRunning).toBe(false));
|
|
@@ -455,7 +451,7 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
455
451
|
);
|
|
456
452
|
|
|
457
453
|
const { result } = renderHook(() =>
|
|
458
|
-
useTrueFoundryAgentMessages({
|
|
454
|
+
useTrueFoundryAgentMessages({ server: mockServer, sessionId: "session-1" }),
|
|
459
455
|
);
|
|
460
456
|
|
|
461
457
|
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
@@ -474,7 +470,7 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
474
470
|
|
|
475
471
|
it("sendTurn appends a user message and streams the assistant reply", async () => {
|
|
476
472
|
const { result } = renderHook(() =>
|
|
477
|
-
useTrueFoundryAgentMessages({
|
|
473
|
+
useTrueFoundryAgentMessages({ server: mockServer, sessionId: "session-1" }),
|
|
478
474
|
);
|
|
479
475
|
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
480
476
|
|
|
@@ -526,12 +522,6 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
526
522
|
}),
|
|
527
523
|
fold,
|
|
528
524
|
});
|
|
529
|
-
vi.mocked(getSession).mockResolvedValue({
|
|
530
|
-
cancel: vi.fn().mockResolvedValue(undefined),
|
|
531
|
-
listTurns: vi.fn(async function* () {}),
|
|
532
|
-
listEvents: vi.fn(async function* () {}),
|
|
533
|
-
} as never);
|
|
534
|
-
|
|
535
525
|
let releaseStream: (() => void) | undefined;
|
|
536
526
|
vi.mocked(streamTurnContent).mockReturnValue(
|
|
537
527
|
(async function* () {
|
|
@@ -545,7 +535,7 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
545
535
|
);
|
|
546
536
|
|
|
547
537
|
const { result } = renderHook(() =>
|
|
548
|
-
useTrueFoundryAgentMessages({
|
|
538
|
+
useTrueFoundryAgentMessages({ server: mockServer, sessionId: "session-1" }),
|
|
549
539
|
);
|
|
550
540
|
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
551
541
|
expect(result.current.messages.map((m) => m.role)).toEqual([
|
|
@@ -635,7 +625,7 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
635
625
|
);
|
|
636
626
|
|
|
637
627
|
const { result } = renderHook(() =>
|
|
638
|
-
useTrueFoundryAgentMessages({
|
|
628
|
+
useTrueFoundryAgentMessages({ server: mockServer, sessionId: "session-1" }),
|
|
639
629
|
);
|
|
640
630
|
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
641
631
|
|
|
@@ -680,7 +670,7 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
680
670
|
);
|
|
681
671
|
|
|
682
672
|
const { result } = renderHook(() =>
|
|
683
|
-
useTrueFoundryAgentMessages({
|
|
673
|
+
useTrueFoundryAgentMessages({ server: mockServer, sessionId: "session-1" }),
|
|
684
674
|
);
|
|
685
675
|
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
686
676
|
|
|
@@ -700,7 +690,7 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
700
690
|
);
|
|
701
691
|
|
|
702
692
|
const { result } = renderHook(() =>
|
|
703
|
-
useTrueFoundryAgentMessages({
|
|
693
|
+
useTrueFoundryAgentMessages({ server: mockServer, sessionId: "session-1" }),
|
|
704
694
|
);
|
|
705
695
|
await waitFor(() => expect(result.current.messages).toHaveLength(1));
|
|
706
696
|
|
|
@@ -718,7 +708,8 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
718
708
|
});
|
|
719
709
|
|
|
720
710
|
expect(streamTurnContent).toHaveBeenCalledWith(
|
|
721
|
-
|
|
711
|
+
mockServer,
|
|
712
|
+
"session-1",
|
|
722
713
|
expect.any(PeerThreadFoldState),
|
|
723
714
|
{
|
|
724
715
|
inputs: [
|
|
@@ -743,7 +734,7 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
743
734
|
);
|
|
744
735
|
|
|
745
736
|
const { result } = renderHook(() =>
|
|
746
|
-
useTrueFoundryAgentMessages({
|
|
737
|
+
useTrueFoundryAgentMessages({ server: mockServer, sessionId: "session-1" }),
|
|
747
738
|
);
|
|
748
739
|
await waitFor(() => expect(result.current.messages).toHaveLength(1));
|
|
749
740
|
|
|
@@ -777,7 +768,7 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
777
768
|
);
|
|
778
769
|
|
|
779
770
|
const { result } = renderHook(() =>
|
|
780
|
-
useTrueFoundryAgentMessages({
|
|
771
|
+
useTrueFoundryAgentMessages({ server: mockServer, sessionId: "session-1" }),
|
|
781
772
|
);
|
|
782
773
|
await waitFor(() => expect(result.current.messages).toHaveLength(1));
|
|
783
774
|
|
|
@@ -799,7 +790,8 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
799
790
|
|
|
800
791
|
await waitFor(() => expect(streamTurnContent).toHaveBeenCalled());
|
|
801
792
|
expect(streamTurnContent).toHaveBeenCalledWith(
|
|
802
|
-
|
|
793
|
+
mockServer,
|
|
794
|
+
"session-1",
|
|
803
795
|
expect.any(PeerThreadFoldState),
|
|
804
796
|
{
|
|
805
797
|
inputs: [
|
|
@@ -836,7 +828,7 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
836
828
|
);
|
|
837
829
|
|
|
838
830
|
const { result } = renderHook(() =>
|
|
839
|
-
useTrueFoundryAgentMessages({
|
|
831
|
+
useTrueFoundryAgentMessages({ server: mockServer, sessionId: "session-1" }),
|
|
840
832
|
);
|
|
841
833
|
await waitFor(() => expect(result.current.messages).toHaveLength(2));
|
|
842
834
|
expect(collectPendingToolResponses(result.current.messages)).toHaveLength(1);
|
|
@@ -858,13 +850,13 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
858
850
|
});
|
|
859
851
|
|
|
860
852
|
describe("batched resume invariant", () => {
|
|
861
|
-
it("issues exactly one
|
|
853
|
+
it("issues exactly one prepareAndExecuteTurn input batch across root and sub-agent threads", async () => {
|
|
862
854
|
vi.mocked(loadSessionSnapshot).mockResolvedValue(
|
|
863
855
|
snapshotWithAssistantMessage(assistantMessageWithMultiThreadPendingActions()),
|
|
864
856
|
);
|
|
865
857
|
|
|
866
858
|
const { result } = renderHook(() =>
|
|
867
|
-
useTrueFoundryAgentMessages({
|
|
859
|
+
useTrueFoundryAgentMessages({ server: mockServer, sessionId: "session-1" }),
|
|
868
860
|
);
|
|
869
861
|
await waitFor(() => expect(result.current.messages).toHaveLength(1));
|
|
870
862
|
|
|
@@ -885,7 +877,8 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
885
877
|
|
|
886
878
|
await waitFor(() => expect(streamTurnContent).toHaveBeenCalledTimes(1));
|
|
887
879
|
expect(streamTurnContent).toHaveBeenCalledWith(
|
|
888
|
-
|
|
880
|
+
mockServer,
|
|
881
|
+
"session-1",
|
|
889
882
|
expect.any(PeerThreadFoldState),
|
|
890
883
|
{
|
|
891
884
|
inputs: [
|
|
@@ -914,7 +907,7 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
914
907
|
);
|
|
915
908
|
|
|
916
909
|
const { result } = renderHook(() =>
|
|
917
|
-
useTrueFoundryAgentMessages({
|
|
910
|
+
useTrueFoundryAgentMessages({ server: mockServer, sessionId: "session-1" }),
|
|
918
911
|
);
|
|
919
912
|
await waitFor(() => expect(result.current.messages).toHaveLength(1));
|
|
920
913
|
|
|
@@ -931,7 +924,7 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
931
924
|
});
|
|
932
925
|
});
|
|
933
926
|
|
|
934
|
-
it("cancel drains the stream gracefully and calls
|
|
927
|
+
it("cancel drains the stream gracefully and calls cancelSession", async () => {
|
|
935
928
|
let resolveStream: (() => void) | undefined;
|
|
936
929
|
vi.mocked(streamTurnContent).mockReturnValue(
|
|
937
930
|
(async function* () {
|
|
@@ -941,15 +934,14 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
941
934
|
});
|
|
942
935
|
})(),
|
|
943
936
|
);
|
|
944
|
-
//
|
|
937
|
+
// cancelSession makes the backend close the SSE stream gracefully,
|
|
945
938
|
// which ends the active iterator on its own.
|
|
946
|
-
|
|
939
|
+
vi.mocked(mockServer.cancelSession).mockImplementation(async () => {
|
|
947
940
|
resolveStream?.();
|
|
948
941
|
});
|
|
949
|
-
vi.mocked(getSession).mockResolvedValue({ cancel } as never);
|
|
950
942
|
|
|
951
943
|
const { result } = renderHook(() =>
|
|
952
|
-
useTrueFoundryAgentMessages({
|
|
944
|
+
useTrueFoundryAgentMessages({ server: mockServer, sessionId: "session-1" }),
|
|
953
945
|
);
|
|
954
946
|
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
955
947
|
|
|
@@ -964,34 +956,10 @@ describe("useTrueFoundryAgentMessages", () => {
|
|
|
964
956
|
await sendPromise;
|
|
965
957
|
});
|
|
966
958
|
|
|
967
|
-
expect(
|
|
959
|
+
expect(mockServer.cancelSession).toHaveBeenCalledWith({ sessionId: "session-1" });
|
|
968
960
|
expect(result.current.isRunning).toBe(false);
|
|
969
961
|
// No reconcile is triggered by cancel; the session was only loaded once
|
|
970
962
|
// on mount and reconciles against the event log on the next page load.
|
|
971
963
|
expect(loadSessionSnapshot).toHaveBeenCalledTimes(1);
|
|
972
964
|
});
|
|
973
|
-
|
|
974
|
-
it("cancel resolves the session through the private client in draft mode", async () => {
|
|
975
|
-
const cancel = vi.fn().mockResolvedValue(undefined);
|
|
976
|
-
vi.mocked(getSession).mockResolvedValue({ cancel } as never);
|
|
977
|
-
const privateClient = {} as PrivateAgentSessionClient;
|
|
978
|
-
|
|
979
|
-
const { result } = renderHook(() =>
|
|
980
|
-
useTrueFoundryAgentMessages({
|
|
981
|
-
client: mockClient,
|
|
982
|
-
sessionId: "draft-session-1",
|
|
983
|
-
privateClient,
|
|
984
|
-
}),
|
|
985
|
-
);
|
|
986
|
-
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
987
|
-
|
|
988
|
-
await act(async () => {
|
|
989
|
-
await result.current.cancel();
|
|
990
|
-
});
|
|
991
|
-
|
|
992
|
-
expect(cancel).toHaveBeenCalled();
|
|
993
|
-
expect(getSession).toHaveBeenCalledWith(mockClient, "draft-session-1", {
|
|
994
|
-
privateClient,
|
|
995
|
-
});
|
|
996
|
-
});
|
|
997
965
|
});
|
|
@@ -3,15 +3,14 @@
|
|
|
3
3
|
import { generateId } from "@assistant-ui/core";
|
|
4
4
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
5
5
|
import type {
|
|
6
|
-
AgentSessionClient,
|
|
7
6
|
McpAuthRequiredEvent,
|
|
8
7
|
ToolApprovalRequiredEvent,
|
|
9
8
|
ToolResponseRequiredEvent,
|
|
10
9
|
Turn,
|
|
11
10
|
TurnInputItem,
|
|
12
11
|
TurnStateDone,
|
|
13
|
-
} from "
|
|
14
|
-
import type {
|
|
12
|
+
} from "./server/index.js";
|
|
13
|
+
import type { AgentChatServer } from "./server/types.js";
|
|
15
14
|
|
|
16
15
|
import { ROOT_THREAD_ID } from "./constants.js";
|
|
17
16
|
import {
|
|
@@ -36,7 +35,6 @@ import {
|
|
|
36
35
|
messageHasPendingRequiredActions,
|
|
37
36
|
type RequiredActionInput,
|
|
38
37
|
} from "./requiredActionInputs.js";
|
|
39
|
-
import { getSession, type GetSessionOptions } from "./sessions.js";
|
|
40
38
|
import {
|
|
41
39
|
createEmptySessionSnapshot,
|
|
42
40
|
replaceSessionSnapshot,
|
|
@@ -56,7 +54,7 @@ import {
|
|
|
56
54
|
import type { TurnStreamUpdate } from "./turnStreamUpdate.js";
|
|
57
55
|
|
|
58
56
|
export type UseTrueFoundryAgentMessagesOptions = {
|
|
59
|
-
|
|
57
|
+
server: AgentChatServer;
|
|
60
58
|
sessionId: string | undefined;
|
|
61
59
|
/** When true the thread is the currently selected (main) thread. */
|
|
62
60
|
isMain?: boolean | undefined;
|
|
@@ -68,8 +66,6 @@ export type UseTrueFoundryAgentMessagesOptions = {
|
|
|
68
66
|
}>;
|
|
69
67
|
/** Maps a thread `remoteId` to the gateway session id used for turns. */
|
|
70
68
|
resolveConversationSessionId?: (remoteId: string) => Promise<string>;
|
|
71
|
-
/** When set, turns bind via PrivateAgentSessionClient.getDraftSession. */
|
|
72
|
-
privateClient?: PrivateAgentSessionClient;
|
|
73
69
|
/**
|
|
74
70
|
* Optional per-turn headers for createTurn. Invoked once per `sendTurn` after
|
|
75
71
|
* the session is resolved; return value is forwarded to `turn.execute`.
|
|
@@ -287,22 +283,15 @@ function resolveTurnInput(
|
|
|
287
283
|
}
|
|
288
284
|
|
|
289
285
|
export function useTrueFoundryAgentMessages({
|
|
290
|
-
|
|
286
|
+
server,
|
|
291
287
|
sessionId,
|
|
292
288
|
isMain,
|
|
293
289
|
listEventsConcurrency,
|
|
294
290
|
onError,
|
|
295
291
|
initializeSession,
|
|
296
292
|
resolveConversationSessionId,
|
|
297
|
-
privateClient,
|
|
298
293
|
getTurnHeaders,
|
|
299
294
|
}: UseTrueFoundryAgentMessagesOptions) {
|
|
300
|
-
const sessionOptions = useMemo<GetSessionOptions | undefined>(
|
|
301
|
-
() =>
|
|
302
|
-
privateClient != null ? { privateClient } : undefined,
|
|
303
|
-
[privateClient],
|
|
304
|
-
);
|
|
305
|
-
|
|
306
295
|
const [snapshot, setSnapshot] = useState<SessionSnapshot>(createEmptySessionSnapshot);
|
|
307
296
|
const [isRunning, setIsRunning] = useState(false);
|
|
308
297
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -508,9 +497,8 @@ export function useTrueFoundryAgentMessages({
|
|
|
508
497
|
resolveConversationSessionIdRef.current,
|
|
509
498
|
);
|
|
510
499
|
const loadedSnapshot = await loadSessionSnapshot(
|
|
511
|
-
|
|
500
|
+
server,
|
|
512
501
|
conversationSessionId,
|
|
513
|
-
sessionOptions,
|
|
514
502
|
(snap) => {
|
|
515
503
|
if (generation === loadGenerationRef.current) {
|
|
516
504
|
setSnapshot(snap);
|
|
@@ -542,7 +530,9 @@ export function useTrueFoundryAgentMessages({
|
|
|
542
530
|
void runStream(
|
|
543
531
|
(signal) =>
|
|
544
532
|
resumeTurnStream(
|
|
545
|
-
|
|
533
|
+
server,
|
|
534
|
+
conversationSessionId,
|
|
535
|
+
turn.id,
|
|
546
536
|
loadedSnapshot.fold,
|
|
547
537
|
signal,
|
|
548
538
|
undefined,
|
|
@@ -562,7 +552,7 @@ export function useTrueFoundryAgentMessages({
|
|
|
562
552
|
setIsLoading(false);
|
|
563
553
|
}
|
|
564
554
|
}
|
|
565
|
-
}, [
|
|
555
|
+
}, [server, runStream, sessionId, loadRetryTrigger, isMain]);
|
|
566
556
|
|
|
567
557
|
useEffect(() => {
|
|
568
558
|
void load().catch(() => undefined);
|
|
@@ -584,7 +574,6 @@ export function useTrueFoundryAgentMessages({
|
|
|
584
574
|
activeSessionId,
|
|
585
575
|
resolveConversationSessionIdRef.current,
|
|
586
576
|
);
|
|
587
|
-
const session = await getSession(client, conversationSessionId, sessionOptions);
|
|
588
577
|
const turnHeaders = await getTurnHeadersRef.current?.();
|
|
589
578
|
const streamHeaders =
|
|
590
579
|
turnHeaders != null ? { headers: turnHeaders } : {};
|
|
@@ -596,7 +585,7 @@ export function useTrueFoundryAgentMessages({
|
|
|
596
585
|
isContinuation && continuationTurnId != null
|
|
597
586
|
? continuationTurnId
|
|
598
587
|
: generateId();
|
|
599
|
-
// First turns must send previousTurnId:
|
|
588
|
+
// First turns must send previousTurnId: "none".
|
|
600
589
|
const isFirstTurnInSession =
|
|
601
590
|
"userMessage" in options &&
|
|
602
591
|
options.previousTurnId === undefined &&
|
|
@@ -673,7 +662,8 @@ export function useTrueFoundryAgentMessages({
|
|
|
673
662
|
(signal) => {
|
|
674
663
|
if ("inputs" in options) {
|
|
675
664
|
return streamTurnContent(
|
|
676
|
-
|
|
665
|
+
server,
|
|
666
|
+
conversationSessionId,
|
|
677
667
|
snapshotRef.current.fold,
|
|
678
668
|
{ inputs: options.inputs, ...streamHeaders },
|
|
679
669
|
signal,
|
|
@@ -682,7 +672,8 @@ export function useTrueFoundryAgentMessages({
|
|
|
682
672
|
}
|
|
683
673
|
if ("resumeMcpAuth" in options) {
|
|
684
674
|
return streamTurnContent(
|
|
685
|
-
|
|
675
|
+
server,
|
|
676
|
+
conversationSessionId,
|
|
686
677
|
snapshotRef.current.fold,
|
|
687
678
|
{ resumeMcpAuth: true, ...streamHeaders },
|
|
688
679
|
signal,
|
|
@@ -690,14 +681,15 @@ export function useTrueFoundryAgentMessages({
|
|
|
690
681
|
);
|
|
691
682
|
}
|
|
692
683
|
return streamTurnContent(
|
|
693
|
-
|
|
684
|
+
server,
|
|
685
|
+
conversationSessionId,
|
|
694
686
|
snapshotRef.current.fold,
|
|
695
687
|
{
|
|
696
688
|
userMessage: options.userMessage,
|
|
697
689
|
...(options.previousTurnId !== undefined
|
|
698
|
-
? { previousTurnId: options.previousTurnId }
|
|
690
|
+
? { previousTurnId: options.previousTurnId ?? "none" }
|
|
699
691
|
: isFirstTurnInSession
|
|
700
|
-
? { previousTurnId:
|
|
692
|
+
? { previousTurnId: "none" }
|
|
701
693
|
: {}),
|
|
702
694
|
...streamHeaders,
|
|
703
695
|
},
|
|
@@ -726,7 +718,7 @@ export function useTrueFoundryAgentMessages({
|
|
|
726
718
|
isContinuation,
|
|
727
719
|
);
|
|
728
720
|
},
|
|
729
|
-
[
|
|
721
|
+
[server, runStream, sessionId],
|
|
730
722
|
);
|
|
731
723
|
|
|
732
724
|
const cancel = useCallback(async () => {
|
|
@@ -738,17 +730,16 @@ export function useTrueFoundryAgentMessages({
|
|
|
738
730
|
sessionId,
|
|
739
731
|
resolveConversationSessionIdRef.current,
|
|
740
732
|
);
|
|
741
|
-
const session = await getSession(client, conversationSessionId, sessionOptions);
|
|
742
733
|
// Request cancellation but keep consuming the stream. After cancel(),
|
|
743
734
|
// the backend gracefully closes the SSE stream: it emits a terminal
|
|
744
735
|
// turn.done event and then ends the stream, which lets the active run
|
|
745
736
|
// drain to completion on its own instead of being torn down mid-flight.
|
|
746
|
-
await
|
|
737
|
+
await server.cancelSession({ sessionId: conversationSessionId }).catch(() => undefined);
|
|
747
738
|
// Wait for the in-flight stream to finish draining. No explicit
|
|
748
739
|
// reconcile is needed here — the cancelled turn is terminal and local
|
|
749
740
|
// state reconciles against the event log on the next session load.
|
|
750
741
|
await activeRunRef.current?.catch(() => undefined);
|
|
751
|
-
}, [
|
|
742
|
+
}, [server, sessionId]);
|
|
752
743
|
|
|
753
744
|
const isRunningRef = useRef(isRunning);
|
|
754
745
|
isRunningRef.current = isRunning;
|
|
@@ -817,7 +808,9 @@ export function useTrueFoundryAgentMessages({
|
|
|
817
808
|
await runStream(
|
|
818
809
|
(signal) =>
|
|
819
810
|
resumeTurnStream(
|
|
820
|
-
|
|
811
|
+
server,
|
|
812
|
+
turn.sessionId,
|
|
813
|
+
turn.id,
|
|
821
814
|
snapshotRef.current.fold,
|
|
822
815
|
signal,
|
|
823
816
|
undefined,
|
|
@@ -826,7 +819,7 @@ export function useTrueFoundryAgentMessages({
|
|
|
826
819
|
{ current: turn.id },
|
|
827
820
|
true,
|
|
828
821
|
);
|
|
829
|
-
}, [runStream]);
|
|
822
|
+
}, [runStream, server]);
|
|
830
823
|
|
|
831
824
|
const branchFromTurn = useCallback(
|
|
832
825
|
async (turnId: string, userMessage: UserMessageContent) => {
|
|
@@ -844,13 +837,14 @@ export function useTrueFoundryAgentMessages({
|
|
|
844
837
|
activeSessionId,
|
|
845
838
|
resolveConversationSessionIdRef.current,
|
|
846
839
|
);
|
|
847
|
-
const session = await getSession(client, conversationSessionId, sessionOptions);
|
|
848
840
|
const previousTurnId = await resolveGatewayBranchPreviousTurnIdForTurn(
|
|
849
|
-
|
|
841
|
+
server,
|
|
842
|
+
conversationSessionId,
|
|
850
843
|
turnId,
|
|
851
844
|
);
|
|
852
845
|
const rewound = await buildSnapshotBeforeTurn(
|
|
853
|
-
|
|
846
|
+
server,
|
|
847
|
+
conversationSessionId,
|
|
854
848
|
turnId,
|
|
855
849
|
listEventsConcurrency,
|
|
856
850
|
);
|
|
@@ -868,11 +862,10 @@ export function useTrueFoundryAgentMessages({
|
|
|
868
862
|
},
|
|
869
863
|
[
|
|
870
864
|
cancel,
|
|
871
|
-
|
|
865
|
+
server,
|
|
872
866
|
listEventsConcurrency,
|
|
873
867
|
sendTurn,
|
|
874
868
|
sessionId,
|
|
875
|
-
sessionOptions,
|
|
876
869
|
],
|
|
877
870
|
);
|
|
878
871
|
|
|
@@ -936,13 +929,9 @@ export function useTrueFoundryAgentMessages({
|
|
|
936
929
|
sessionId,
|
|
937
930
|
resolveConversationSessionIdRef.current,
|
|
938
931
|
);
|
|
939
|
-
const session = await getSession(
|
|
940
|
-
client,
|
|
941
|
-
conversationSessionId,
|
|
942
|
-
sessionOptions,
|
|
943
|
-
);
|
|
944
932
|
const next = await prependOlderSessionHistory(
|
|
945
|
-
|
|
933
|
+
server,
|
|
934
|
+
conversationSessionId,
|
|
946
935
|
snapshotRef.current,
|
|
947
936
|
);
|
|
948
937
|
if (generation !== loadGenerationRef.current) {
|
|
@@ -964,7 +953,7 @@ export function useTrueFoundryAgentMessages({
|
|
|
964
953
|
|
|
965
954
|
loadOlderInflightRef.current = run;
|
|
966
955
|
return run;
|
|
967
|
-
}, [
|
|
956
|
+
}, [server, isMain, sessionId]);
|
|
968
957
|
|
|
969
958
|
return {
|
|
970
959
|
messages,
|
|
@@ -986,4 +975,3 @@ export function useTrueFoundryAgentMessages({
|
|
|
986
975
|
}
|
|
987
976
|
|
|
988
977
|
export { findPausedAssistantMessage, MCP_AUTH_RESUME_RUN_CUSTOM_KEY };
|
|
989
|
-
|