@truefoundry/assistant-ui-runtime 0.1.0-rc.1
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 +567 -0
- package/dist/index.d.ts +300 -0
- package/dist/index.js +4440 -0
- package/dist/index.js.map +1 -0
- package/package.json +70 -0
- package/src/agentSessionModule.d.ts +25 -0
- package/src/agentSpec.ts +44 -0
- package/src/askUserQuestion.ts +37 -0
- package/src/attachmentAdapter.test.ts +56 -0
- package/src/attachmentAdapter.ts +58 -0
- package/src/bindDraftAgentSession.test.ts +55 -0
- package/src/bindDraftAgentSession.ts +66 -0
- package/src/buildEditedUserMessageContent.test.ts +61 -0
- package/src/collectPending.test.ts +69 -0
- package/src/collectPending.ts +165 -0
- package/src/constants.ts +2 -0
- package/src/convertTurnMessages.test.ts +1991 -0
- package/src/convertTurnMessages.ts +1251 -0
- package/src/createSubAgent.ts +8 -0
- package/src/draftAgentConfig.test.ts +88 -0
- package/src/draftSessionBridge.ts +28 -0
- package/src/extractTurnUserText.ts +21 -0
- package/src/foldPeerThreads.test.ts +386 -0
- package/src/foldPeerThreads.ts +587 -0
- package/src/hooks.ts +123 -0
- package/src/index.ts +68 -0
- package/src/lastUserMessageText.ts +19 -0
- package/src/loadSessionSnapshot.test.ts +57 -0
- package/src/loadSessionSnapshot.ts +35 -0
- package/src/mcpAuth.ts +44 -0
- package/src/messageCustomMetadata.ts +37 -0
- package/src/modelMessageContent.ts +135 -0
- package/src/modelMessageImageContent.test.ts +109 -0
- package/src/modelMessageImageContent.ts +193 -0
- package/src/requiredActionInputs.test.ts +394 -0
- package/src/requiredActionInputs.ts +65 -0
- package/src/requiredActionsFromActiveUpdate.test.ts +95 -0
- package/src/sessionListStartTimestamp.ts +6 -0
- package/src/sessionSnapshot.ts +107 -0
- package/src/sessions.ts +36 -0
- package/src/streamTurn.test.ts +243 -0
- package/src/streamTurn.ts +119 -0
- package/src/toolApproval.test.ts +137 -0
- package/src/toolApproval.ts +459 -0
- package/src/toolResponse.test.ts +136 -0
- package/src/toolResponse.ts +427 -0
- package/src/truefoundryDraftThreadListAdapter.test.ts +140 -0
- package/src/truefoundryDraftThreadListAdapter.ts +63 -0
- package/src/truefoundryExtras.ts +45 -0
- package/src/truefoundryThreadListAdapter.test.ts +103 -0
- package/src/truefoundryThreadListAdapter.ts +59 -0
- package/src/turnEventHelpers.ts +98 -0
- package/src/turnStreamUpdate.ts +11 -0
- package/src/types.ts +92 -0
- package/src/useDraftAgentSpec.ts +173 -0
- package/src/useTrueFoundryAgentMessages.test.tsx +723 -0
- package/src/useTrueFoundryAgentMessages.ts +757 -0
- package/src/useTrueFoundryAgentRuntime.ts +268 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
pickExternalStoreSharedOptions,
|
|
5
|
+
type AppendMessage,
|
|
6
|
+
type ToolExecutionStatus,
|
|
7
|
+
} from "@assistant-ui/core";
|
|
8
|
+
import {
|
|
9
|
+
useExternalStoreRuntime,
|
|
10
|
+
useRemoteThreadListRuntime,
|
|
11
|
+
useRuntimeAdapters,
|
|
12
|
+
} from "@assistant-ui/core/react";
|
|
13
|
+
import { useAui, useAuiState } from "@assistant-ui/store";
|
|
14
|
+
import type { MutableRefObject } from "react";
|
|
15
|
+
import { useCallback, useMemo, useRef, useState } from "react";
|
|
16
|
+
|
|
17
|
+
import type { AgentSpec } from "./agentSpec.js";
|
|
18
|
+
import {
|
|
19
|
+
collectPendingApprovals,
|
|
20
|
+
collectPendingToolResponses,
|
|
21
|
+
derivePendingMcpAuth,
|
|
22
|
+
deriveSandboxId,
|
|
23
|
+
} from "./collectPending.js";
|
|
24
|
+
import {
|
|
25
|
+
buildUserMessageContent,
|
|
26
|
+
extractEditedText,
|
|
27
|
+
parseTurnIdFromMessageId,
|
|
28
|
+
} from "./convertTurnMessages.js";
|
|
29
|
+
import { createDraftSessionBridge } from "./draftSessionBridge.js";
|
|
30
|
+
import { MCP_AUTH_RESUME_RUN_CUSTOM_KEY } from "./mcpAuth.js";
|
|
31
|
+
import { createTrueFoundryDraftThreadListAdapter } from "./truefoundryDraftThreadListAdapter.js";
|
|
32
|
+
import { trueFoundryExtras } from "./truefoundryExtras.js";
|
|
33
|
+
import { createTrueFoundryThreadListAdapter } from "./truefoundryThreadListAdapter.js";
|
|
34
|
+
import type { UseTrueFoundryAgentRuntimeOptions } from "./types.js";
|
|
35
|
+
import { resolveTrueFoundryAgentRuntimeOptions } from "./types.js";
|
|
36
|
+
import { useDraftAgentSpec } from "./useDraftAgentSpec.js";
|
|
37
|
+
import { useTrueFoundryAgentMessages } from "./useTrueFoundryAgentMessages.js";
|
|
38
|
+
|
|
39
|
+
function useTrueFoundryAgentRuntimeImpl(
|
|
40
|
+
options: ReturnType<typeof resolveTrueFoundryAgentRuntimeOptions>,
|
|
41
|
+
pendingAgentSpecRef: MutableRefObject<AgentSpec | undefined>,
|
|
42
|
+
) {
|
|
43
|
+
const {
|
|
44
|
+
client,
|
|
45
|
+
agent,
|
|
46
|
+
gateway,
|
|
47
|
+
adapters,
|
|
48
|
+
onError,
|
|
49
|
+
listEventsConcurrency,
|
|
50
|
+
...sharedOptions
|
|
51
|
+
} = options;
|
|
52
|
+
|
|
53
|
+
const draftBridgeRef = useRef(
|
|
54
|
+
agent.mode === "draft" && gateway != null
|
|
55
|
+
? createDraftSessionBridge(gateway)
|
|
56
|
+
: null,
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const draftSessionId = useAuiState(
|
|
60
|
+
(state) =>
|
|
61
|
+
agent.mode === "draft"
|
|
62
|
+
? (state.threadListItem.remoteId ?? undefined)
|
|
63
|
+
: undefined,
|
|
64
|
+
);
|
|
65
|
+
const sessionId = useAuiState((state) => state.threadListItem.remoteId ?? undefined);
|
|
66
|
+
|
|
67
|
+
const draftSpec = useDraftAgentSpec({
|
|
68
|
+
draftSessionId,
|
|
69
|
+
draftBridge: draftBridgeRef.current,
|
|
70
|
+
defaultAgentSpec:
|
|
71
|
+
agent.mode === "draft" ? agent.defaultAgentSpec : { model: { name: "" } },
|
|
72
|
+
onAgentSpecChange: agent.mode === "draft" ? agent.onAgentSpecChange : undefined,
|
|
73
|
+
onError,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const aui = useAui();
|
|
77
|
+
const initializeSession = useCallback(
|
|
78
|
+
() => aui.threadListItem().initialize(),
|
|
79
|
+
[aui],
|
|
80
|
+
);
|
|
81
|
+
const runtimeAdapters = useRuntimeAdapters();
|
|
82
|
+
const [toolStatuses, setToolStatuses] = useState<
|
|
83
|
+
Record<string, ToolExecutionStatus>
|
|
84
|
+
>({});
|
|
85
|
+
|
|
86
|
+
const {
|
|
87
|
+
messages,
|
|
88
|
+
isRunning,
|
|
89
|
+
isLoading,
|
|
90
|
+
sendTurn,
|
|
91
|
+
cancel,
|
|
92
|
+
respondToToolApproval,
|
|
93
|
+
respondToToolResponse,
|
|
94
|
+
resumeRun,
|
|
95
|
+
editFromTurn,
|
|
96
|
+
resetFromTurn,
|
|
97
|
+
} = useTrueFoundryAgentMessages({
|
|
98
|
+
client,
|
|
99
|
+
sessionId,
|
|
100
|
+
listEventsConcurrency,
|
|
101
|
+
onError,
|
|
102
|
+
initializeSession,
|
|
103
|
+
draftGateway: agent.mode === "draft" ? gateway : undefined,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (agent.mode === "draft" && draftSpec.agentSpec != null) {
|
|
107
|
+
pendingAgentSpecRef.current = draftSpec.agentSpec;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const pendingApprovals = useMemo(
|
|
111
|
+
() => collectPendingApprovals(messages),
|
|
112
|
+
[messages],
|
|
113
|
+
);
|
|
114
|
+
const pendingToolResponses = useMemo(
|
|
115
|
+
() => collectPendingToolResponses(messages),
|
|
116
|
+
[messages],
|
|
117
|
+
);
|
|
118
|
+
const pendingMcpAuth = useMemo(() => derivePendingMcpAuth(messages), [messages]);
|
|
119
|
+
const sandboxId = useMemo(() => deriveSandboxId(messages), [messages]);
|
|
120
|
+
|
|
121
|
+
const resumeMcpAuth = useMemo(
|
|
122
|
+
() => () => sendTurn({ resumeMcpAuth: true }),
|
|
123
|
+
[sendTurn],
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const downloadSandboxFile = useCallback(
|
|
127
|
+
async (path: string) => {
|
|
128
|
+
if (gateway == null) {
|
|
129
|
+
const error = new Error(
|
|
130
|
+
"Downloading a sandbox file requires a `gateway` TrueFoundryGateway client.",
|
|
131
|
+
);
|
|
132
|
+
onError?.(error);
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
if (sandboxId == null) {
|
|
136
|
+
const error = new Error("No sandbox is available yet for this session.");
|
|
137
|
+
onError?.(error);
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
const response = await gateway.agents.downloadSandboxFile(sandboxId, { path });
|
|
141
|
+
return await response.blob();
|
|
142
|
+
},
|
|
143
|
+
[gateway, sandboxId, onError],
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const draftExtras = useMemo(() => {
|
|
147
|
+
if (agent.mode !== "draft") {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
agentSpec: draftSpec.agentSpec,
|
|
152
|
+
draftSessionId: draftSpec.draftSessionId,
|
|
153
|
+
isSpecSyncing: draftSpec.isSpecSyncing,
|
|
154
|
+
specError: draftSpec.specError,
|
|
155
|
+
updateAgentSpec: draftSpec.updateAgentSpec,
|
|
156
|
+
};
|
|
157
|
+
}, [agent.mode, draftSpec]);
|
|
158
|
+
|
|
159
|
+
return useExternalStoreRuntime({
|
|
160
|
+
...pickExternalStoreSharedOptions(sharedOptions),
|
|
161
|
+
messages,
|
|
162
|
+
isRunning,
|
|
163
|
+
isLoading,
|
|
164
|
+
extras: trueFoundryExtras.provide({
|
|
165
|
+
pendingApprovals,
|
|
166
|
+
pendingToolResponses,
|
|
167
|
+
pendingMcpAuth,
|
|
168
|
+
sandboxId,
|
|
169
|
+
respondToToolApproval,
|
|
170
|
+
respondToToolResponse,
|
|
171
|
+
resumeMcpAuth,
|
|
172
|
+
downloadSandboxFile,
|
|
173
|
+
cancel,
|
|
174
|
+
resetFromTurn: (turnId: string) =>
|
|
175
|
+
resetFromTurn(turnId).catch((error) => {
|
|
176
|
+
onError?.(error);
|
|
177
|
+
}),
|
|
178
|
+
draft: draftExtras,
|
|
179
|
+
}),
|
|
180
|
+
unstable_enableToolInvocations: true,
|
|
181
|
+
setToolStatuses,
|
|
182
|
+
adapters: {
|
|
183
|
+
attachments: adapters?.attachments ?? runtimeAdapters?.attachments,
|
|
184
|
+
speech: adapters?.speech,
|
|
185
|
+
dictation: adapters?.dictation,
|
|
186
|
+
voice: adapters?.voice,
|
|
187
|
+
feedback: adapters?.feedback,
|
|
188
|
+
},
|
|
189
|
+
onNew: async (message: AppendMessage) => {
|
|
190
|
+
if (!(message.startRun ?? message.role === "user")) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const resumeMcpAuthFlag =
|
|
195
|
+
message.runConfig?.custom?.[MCP_AUTH_RESUME_RUN_CUSTOM_KEY] === true;
|
|
196
|
+
|
|
197
|
+
if (resumeMcpAuthFlag) {
|
|
198
|
+
await sendTurn({ resumeMcpAuth: true });
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
await sendTurn({ userMessage: buildUserMessageContent(message) });
|
|
203
|
+
},
|
|
204
|
+
onCancel: async () => {
|
|
205
|
+
await cancel();
|
|
206
|
+
},
|
|
207
|
+
onRespondToToolApproval: async (response) => {
|
|
208
|
+
respondToToolApproval(response);
|
|
209
|
+
},
|
|
210
|
+
onResume: async () => {
|
|
211
|
+
await resumeRun();
|
|
212
|
+
},
|
|
213
|
+
onEdit: async (message: AppendMessage) => {
|
|
214
|
+
const sourceId = message.sourceId;
|
|
215
|
+
if (sourceId == null) {
|
|
216
|
+
throw new Error("Could not resolve edited user message.");
|
|
217
|
+
}
|
|
218
|
+
const turnId = parseTurnIdFromMessageId(sourceId);
|
|
219
|
+
const editedText = extractEditedText(message);
|
|
220
|
+
try {
|
|
221
|
+
await editFromTurn(turnId, editedText);
|
|
222
|
+
} catch (error) {
|
|
223
|
+
onError?.(error);
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function useTrueFoundryAgentRuntime(options: UseTrueFoundryAgentRuntimeOptions) {
|
|
231
|
+
const resolved = useMemo(
|
|
232
|
+
() => resolveTrueFoundryAgentRuntimeOptions(options),
|
|
233
|
+
[options],
|
|
234
|
+
);
|
|
235
|
+
const { client, agent, gateway } = resolved;
|
|
236
|
+
|
|
237
|
+
const pendingAgentSpecRef = useRef<AgentSpec | undefined>(
|
|
238
|
+
agent.mode === "draft" ? agent.defaultAgentSpec : undefined,
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
const threadListAdapter = useMemo(() => {
|
|
242
|
+
if (agent.mode === "draft") {
|
|
243
|
+
if (gateway == null) {
|
|
244
|
+
throw new Error(
|
|
245
|
+
"Draft agent mode requires a `gateway` TrueFoundryGateway client.",
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
return createTrueFoundryDraftThreadListAdapter({
|
|
249
|
+
gateway,
|
|
250
|
+
defaultAgentSpec: agent.defaultAgentSpec,
|
|
251
|
+
getAgentSpec: () => pendingAgentSpecRef.current ?? agent.defaultAgentSpec,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
return createTrueFoundryThreadListAdapter({
|
|
255
|
+
client,
|
|
256
|
+
agentName: agent.agentName,
|
|
257
|
+
});
|
|
258
|
+
}, [agent, client, gateway]);
|
|
259
|
+
|
|
260
|
+
return useRemoteThreadListRuntime({
|
|
261
|
+
allowNesting: true,
|
|
262
|
+
adapter: threadListAdapter,
|
|
263
|
+
initialThreadId: resolved.initialSessionId,
|
|
264
|
+
threadId: resolved.threadId,
|
|
265
|
+
onThreadIdChange: resolved.onThreadIdChange,
|
|
266
|
+
runtimeHook: () => useTrueFoundryAgentRuntimeImpl(resolved, pendingAgentSpecRef),
|
|
267
|
+
});
|
|
268
|
+
}
|