@truefoundry/assistant-ui-runtime 0.1.1 → 0.1.2
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 +31 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.js +470 -112
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/convertTurnMessages.test.ts +146 -7
- package/src/convertTurnMessages.ts +442 -164
- package/src/hooks.ts +18 -0
- package/src/index.ts +1 -0
- package/src/private/bindDraftAgentSession.test.ts +3 -2
- package/src/private/draftSessionBridge.ts +8 -2
- package/src/private/useDraftAgentSpec.test.tsx +153 -0
- package/src/private/useDraftAgentSpec.ts +80 -3
- package/src/sessionSnapshot.ts +21 -1
- package/src/streamTurn.test.ts +34 -0
- package/src/streamTurn.ts +9 -1
- package/src/truefoundryExtras.ts +3 -0
- package/src/useTrueFoundryAgentMessages.test.tsx +50 -0
- package/src/useTrueFoundryAgentMessages.ts +85 -10
- package/src/useTrueFoundryAgentRuntime.ts +25 -1
|
@@ -16,11 +16,12 @@ import type { TrueFoundryGateway } from "truefoundry-gateway-sdk";
|
|
|
16
16
|
import { ROOT_THREAD_ID } from "./constants.js";
|
|
17
17
|
import {
|
|
18
18
|
buildEditedUserMessageContent,
|
|
19
|
-
|
|
19
|
+
buildSnapshotBeforeTurn,
|
|
20
20
|
computeGroupRootBaseline,
|
|
21
21
|
extractTurnUserMessageContent,
|
|
22
|
+
prependOlderSessionHistory,
|
|
22
23
|
projectSessionMessages,
|
|
23
|
-
|
|
24
|
+
resolveGatewayBranchPreviousTurnIdForTurn,
|
|
24
25
|
rootModelMessageIdsSinceBaseline,
|
|
25
26
|
userMessageContentToText,
|
|
26
27
|
type UserMessageContent,
|
|
@@ -69,6 +70,11 @@ export type UseTrueFoundryAgentMessagesOptions = {
|
|
|
69
70
|
resolveConversationSessionId?: (remoteId: string) => Promise<string>;
|
|
70
71
|
/** When set, turns bind to `/agents/sessions/{draftSessionId}/turns` after draft validation. */
|
|
71
72
|
draftGateway?: TrueFoundryGateway;
|
|
73
|
+
/**
|
|
74
|
+
* Optional per-turn headers for createTurn. Invoked once per `sendTurn` after
|
|
75
|
+
* the session is resolved; return value is forwarded to `turn.execute`.
|
|
76
|
+
*/
|
|
77
|
+
getTurnHeaders?: () => Promise<Record<string, string> | undefined>;
|
|
72
78
|
};
|
|
73
79
|
|
|
74
80
|
export type SendTurnOptions =
|
|
@@ -289,6 +295,7 @@ export function useTrueFoundryAgentMessages({
|
|
|
289
295
|
initializeSession,
|
|
290
296
|
resolveConversationSessionId,
|
|
291
297
|
draftGateway,
|
|
298
|
+
getTurnHeaders,
|
|
292
299
|
}: UseTrueFoundryAgentMessagesOptions) {
|
|
293
300
|
const sessionOptions = useMemo<GetSessionOptions | undefined>(
|
|
294
301
|
() =>
|
|
@@ -299,10 +306,12 @@ export function useTrueFoundryAgentMessages({
|
|
|
299
306
|
const [snapshot, setSnapshot] = useState<SessionSnapshot>(createEmptySessionSnapshot);
|
|
300
307
|
const [isRunning, setIsRunning] = useState(false);
|
|
301
308
|
const [isLoading, setIsLoading] = useState(false);
|
|
309
|
+
const [isLoadingOlderHistory, setIsLoadingOlderHistory] = useState(false);
|
|
302
310
|
const [loadRetryTrigger, setLoadRetryTrigger] = useState(0);
|
|
303
311
|
|
|
304
312
|
const snapshotRef = useRef(snapshot);
|
|
305
313
|
snapshotRef.current = snapshot;
|
|
314
|
+
const loadOlderInflightRef = useRef<Promise<void> | null>(null);
|
|
306
315
|
|
|
307
316
|
const onErrorRef = useRef(onError);
|
|
308
317
|
onErrorRef.current = onError;
|
|
@@ -310,6 +319,8 @@ export function useTrueFoundryAgentMessages({
|
|
|
310
319
|
resolveConversationSessionIdRef.current = resolveConversationSessionId;
|
|
311
320
|
const initializeSessionRef = useRef(initializeSession);
|
|
312
321
|
initializeSessionRef.current = initializeSession;
|
|
322
|
+
const getTurnHeadersRef = useRef(getTurnHeaders);
|
|
323
|
+
getTurnHeadersRef.current = getTurnHeaders;
|
|
313
324
|
|
|
314
325
|
const createdAtByMessageIdRef = useRef(new Map<string, Date>());
|
|
315
326
|
const abortControllerRef = useRef<AbortController | null>(null);
|
|
@@ -477,9 +488,11 @@ export function useTrueFoundryAgentMessages({
|
|
|
477
488
|
const generation = ++loadGenerationRef.current;
|
|
478
489
|
++streamGenerationRef.current;
|
|
479
490
|
abortControllerRef.current?.abort();
|
|
491
|
+
loadOlderInflightRef.current = null;
|
|
480
492
|
createdAtByMessageIdRef.current = new Map();
|
|
481
493
|
setSnapshot(createEmptySessionSnapshot());
|
|
482
494
|
setIsLoading(true);
|
|
495
|
+
setIsLoadingOlderHistory(false);
|
|
483
496
|
|
|
484
497
|
try {
|
|
485
498
|
const conversationSessionId = await resolveActiveSessionId(
|
|
@@ -564,6 +577,9 @@ export function useTrueFoundryAgentMessages({
|
|
|
564
577
|
resolveConversationSessionIdRef.current,
|
|
565
578
|
);
|
|
566
579
|
const session = await getSession(client, conversationSessionId, sessionOptions);
|
|
580
|
+
const turnHeaders = await getTurnHeadersRef.current?.();
|
|
581
|
+
const streamHeaders =
|
|
582
|
+
turnHeaders != null ? { headers: turnHeaders } : {};
|
|
567
583
|
const isContinuation =
|
|
568
584
|
"inputs" in options ||
|
|
569
585
|
("resumeMcpAuth" in options && options.resumeMcpAuth === true);
|
|
@@ -639,7 +655,7 @@ export function useTrueFoundryAgentMessages({
|
|
|
639
655
|
return streamTurnContent(
|
|
640
656
|
session,
|
|
641
657
|
snapshotRef.current.fold,
|
|
642
|
-
{ inputs: options.inputs },
|
|
658
|
+
{ inputs: options.inputs, ...streamHeaders },
|
|
643
659
|
signal,
|
|
644
660
|
groupRootBaseline,
|
|
645
661
|
);
|
|
@@ -648,7 +664,7 @@ export function useTrueFoundryAgentMessages({
|
|
|
648
664
|
return streamTurnContent(
|
|
649
665
|
session,
|
|
650
666
|
snapshotRef.current.fold,
|
|
651
|
-
{ resumeMcpAuth: true },
|
|
667
|
+
{ resumeMcpAuth: true, ...streamHeaders },
|
|
652
668
|
signal,
|
|
653
669
|
groupRootBaseline,
|
|
654
670
|
);
|
|
@@ -661,6 +677,7 @@ export function useTrueFoundryAgentMessages({
|
|
|
661
677
|
...(options.previousTurnId !== undefined
|
|
662
678
|
? { previousTurnId: options.previousTurnId }
|
|
663
679
|
: {}),
|
|
680
|
+
...streamHeaders,
|
|
664
681
|
},
|
|
665
682
|
signal,
|
|
666
683
|
groupRootBaseline,
|
|
@@ -782,8 +799,6 @@ export function useTrueFoundryAgentMessages({
|
|
|
782
799
|
const committed = commitActiveStream(snapshotRef.current);
|
|
783
800
|
setSnapshot(committed);
|
|
784
801
|
|
|
785
|
-
const turnIndex = findTurnIndex(committed, turnId);
|
|
786
|
-
|
|
787
802
|
await cancel();
|
|
788
803
|
|
|
789
804
|
const conversationSessionId = await resolveActiveSessionId(
|
|
@@ -791,13 +806,13 @@ export function useTrueFoundryAgentMessages({
|
|
|
791
806
|
resolveConversationSessionIdRef.current,
|
|
792
807
|
);
|
|
793
808
|
const session = await getSession(client, conversationSessionId, sessionOptions);
|
|
794
|
-
const previousTurnId = await
|
|
809
|
+
const previousTurnId = await resolveGatewayBranchPreviousTurnIdForTurn(
|
|
795
810
|
session,
|
|
796
|
-
|
|
811
|
+
turnId,
|
|
797
812
|
);
|
|
798
|
-
const rewound = await
|
|
813
|
+
const rewound = await buildSnapshotBeforeTurn(
|
|
799
814
|
session,
|
|
800
|
-
|
|
815
|
+
turnId,
|
|
801
816
|
listEventsConcurrency,
|
|
802
817
|
);
|
|
803
818
|
createdAtByMessageIdRef.current = new Map();
|
|
@@ -855,10 +870,70 @@ export function useTrueFoundryAgentMessages({
|
|
|
855
870
|
setLoadRetryTrigger((n) => n + 1);
|
|
856
871
|
}, []);
|
|
857
872
|
|
|
873
|
+
const hasOlderHistory = snapshot.historyPagination?.hasOlder === true;
|
|
874
|
+
|
|
875
|
+
const loadOlderHistory = useCallback(async () => {
|
|
876
|
+
if (sessionId == null || isMain === false) {
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
if (loadOlderInflightRef.current != null) {
|
|
880
|
+
return loadOlderInflightRef.current;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
const current = snapshotRef.current;
|
|
884
|
+
if (current.historyPagination?.hasOlder !== true) {
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
if (current.historyPagination.olderPageToken == null) {
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
const generation = loadGenerationRef.current;
|
|
892
|
+
setIsLoadingOlderHistory(true);
|
|
893
|
+
|
|
894
|
+
const run = (async () => {
|
|
895
|
+
try {
|
|
896
|
+
const conversationSessionId = await resolveActiveSessionId(
|
|
897
|
+
sessionId,
|
|
898
|
+
resolveConversationSessionIdRef.current,
|
|
899
|
+
);
|
|
900
|
+
const session = await getSession(
|
|
901
|
+
client,
|
|
902
|
+
conversationSessionId,
|
|
903
|
+
sessionOptions,
|
|
904
|
+
);
|
|
905
|
+
const next = await prependOlderSessionHistory(
|
|
906
|
+
session,
|
|
907
|
+
snapshotRef.current,
|
|
908
|
+
);
|
|
909
|
+
if (generation !== loadGenerationRef.current) {
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
setSnapshot(next);
|
|
913
|
+
} catch (error) {
|
|
914
|
+
if (generation === loadGenerationRef.current) {
|
|
915
|
+
onErrorRef.current?.(error);
|
|
916
|
+
}
|
|
917
|
+
throw error;
|
|
918
|
+
} finally {
|
|
919
|
+
if (generation === loadGenerationRef.current) {
|
|
920
|
+
setIsLoadingOlderHistory(false);
|
|
921
|
+
}
|
|
922
|
+
loadOlderInflightRef.current = null;
|
|
923
|
+
}
|
|
924
|
+
})();
|
|
925
|
+
|
|
926
|
+
loadOlderInflightRef.current = run;
|
|
927
|
+
return run;
|
|
928
|
+
}, [client, isMain, sessionId, sessionOptions]);
|
|
929
|
+
|
|
858
930
|
return {
|
|
859
931
|
messages,
|
|
860
932
|
isRunning,
|
|
861
933
|
isLoading,
|
|
934
|
+
isLoadingOlderHistory,
|
|
935
|
+
hasOlderHistory,
|
|
936
|
+
loadOlderHistory,
|
|
862
937
|
retryLoad,
|
|
863
938
|
sendTurn,
|
|
864
939
|
cancel,
|
|
@@ -26,7 +26,10 @@ import {
|
|
|
26
26
|
extractEditedText,
|
|
27
27
|
parseTurnIdFromMessageId,
|
|
28
28
|
} from "./convertTurnMessages.js";
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
createDraftSessionBridge,
|
|
31
|
+
DRAFT_SESSION_LAST_UPDATED_AT_HEADER,
|
|
32
|
+
} from "./private/draftSessionBridge.js";
|
|
30
33
|
import { MCP_AUTH_RESUME_RUN_CUSTOM_KEY } from "./mcpAuth.js";
|
|
31
34
|
import { createTrueFoundryDraftThreadListAdapter } from "./private/truefoundryDraftThreadListAdapter.js";
|
|
32
35
|
import { trueFoundryExtras } from "./truefoundryExtras.js";
|
|
@@ -76,6 +79,20 @@ function useTrueFoundryAgentRuntimeImpl(
|
|
|
76
79
|
onError,
|
|
77
80
|
});
|
|
78
81
|
|
|
82
|
+
const takeTurnHeaderTimestampRef = useRef(draftSpec.takeTurnHeaderTimestamp);
|
|
83
|
+
takeTurnHeaderTimestampRef.current = draftSpec.takeTurnHeaderTimestamp;
|
|
84
|
+
|
|
85
|
+
const getTurnHeaders = useCallback(async () => {
|
|
86
|
+
if (agent.mode !== "draft") {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
const updatedAt = await takeTurnHeaderTimestampRef.current();
|
|
90
|
+
if (updatedAt == null) {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
return { [DRAFT_SESSION_LAST_UPDATED_AT_HEADER]: updatedAt };
|
|
94
|
+
}, [agent.mode]);
|
|
95
|
+
|
|
79
96
|
const aui = useAui();
|
|
80
97
|
const initializeSession = useCallback(
|
|
81
98
|
() => aui.threadListItem().initialize(),
|
|
@@ -90,6 +107,9 @@ function useTrueFoundryAgentRuntimeImpl(
|
|
|
90
107
|
messages,
|
|
91
108
|
isRunning,
|
|
92
109
|
isLoading,
|
|
110
|
+
isLoadingOlderHistory,
|
|
111
|
+
hasOlderHistory,
|
|
112
|
+
loadOlderHistory,
|
|
93
113
|
sendTurn,
|
|
94
114
|
cancel,
|
|
95
115
|
respondToToolApproval,
|
|
@@ -106,6 +126,7 @@ function useTrueFoundryAgentRuntimeImpl(
|
|
|
106
126
|
onError,
|
|
107
127
|
initializeSession,
|
|
108
128
|
draftGateway: agent.mode === "draft" ? gateway : undefined,
|
|
129
|
+
getTurnHeaders: agent.mode === "draft" ? getTurnHeaders : undefined,
|
|
109
130
|
});
|
|
110
131
|
|
|
111
132
|
if (agent.mode === "draft" && draftSpec.agentSpec != null) {
|
|
@@ -181,6 +202,9 @@ function useTrueFoundryAgentRuntimeImpl(
|
|
|
181
202
|
onError?.(error);
|
|
182
203
|
}),
|
|
183
204
|
reload: retryLoad,
|
|
205
|
+
hasOlderHistory,
|
|
206
|
+
isLoadingOlderHistory,
|
|
207
|
+
loadOlderHistory,
|
|
184
208
|
draft: draftExtras,
|
|
185
209
|
}),
|
|
186
210
|
unstable_enableToolInvocations: true,
|