@truefoundry/assistant-ui-runtime 0.1.0-rc.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.
Files changed (33) hide show
  1. package/README.md +45 -77
  2. package/dist/index.d.ts +14 -2
  3. package/dist/index.js +697 -147
  4. package/dist/index.js.map +1 -1
  5. package/package.json +2 -2
  6. package/src/agentSessionModule.d.ts +14 -2
  7. package/src/convertTurnMessages.test.ts +502 -1
  8. package/src/convertTurnMessages.ts +471 -28
  9. package/src/createSubAgent.ts +13 -5
  10. package/src/draftAgentConfig.test.ts +1 -1
  11. package/src/foldPeerThreads.test.ts +56 -0
  12. package/src/foldPeerThreads.ts +7 -1
  13. package/src/hooks.ts +24 -0
  14. package/src/index.ts +7 -5
  15. package/src/loadSessionSnapshot.test.ts +21 -6
  16. package/src/loadSessionSnapshot.ts +9 -5
  17. package/src/{bindDraftAgentSession.test.ts → private/bindDraftAgentSession.test.ts} +3 -2
  18. package/src/{draftSessionBridge.ts → private/draftSessionBridge.ts} +8 -2
  19. package/src/{truefoundryDraftThreadListAdapter.ts → private/truefoundryDraftThreadListAdapter.ts} +1 -1
  20. package/src/private/useDraftAgentSpec.test.tsx +153 -0
  21. package/src/{useDraftAgentSpec.ts → private/useDraftAgentSpec.ts} +80 -3
  22. package/src/sessionSnapshot.ts +48 -2
  23. package/src/sessions.ts +1 -1
  24. package/src/streamTurn.test.ts +34 -0
  25. package/src/streamTurn.ts +9 -1
  26. package/src/truefoundryExtras.ts +5 -1
  27. package/src/types.ts +1 -1
  28. package/src/useTrueFoundryAgentMessages.test.tsx +275 -1
  29. package/src/useTrueFoundryAgentMessages.ts +263 -70
  30. package/src/useTrueFoundryAgentRuntime.ts +51 -13
  31. /package/src/{agentSpec.ts → private/agentSpec.ts} +0 -0
  32. /package/src/{bindDraftAgentSession.ts → private/bindDraftAgentSession.ts} +0 -0
  33. /package/src/{truefoundryDraftThreadListAdapter.test.ts → private/truefoundryDraftThreadListAdapter.test.ts} +0 -0
@@ -14,7 +14,7 @@ import { useAui, useAuiState } from "@assistant-ui/store";
14
14
  import type { MutableRefObject } from "react";
15
15
  import { useCallback, useMemo, useRef, useState } from "react";
16
16
 
17
- import type { AgentSpec } from "./agentSpec.js";
17
+ import type { AgentSpec } from "./private/agentSpec.js";
18
18
  import {
19
19
  collectPendingApprovals,
20
20
  collectPendingToolResponses,
@@ -26,14 +26,17 @@ import {
26
26
  extractEditedText,
27
27
  parseTurnIdFromMessageId,
28
28
  } from "./convertTurnMessages.js";
29
- import { createDraftSessionBridge } from "./draftSessionBridge.js";
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
- import { createTrueFoundryDraftThreadListAdapter } from "./truefoundryDraftThreadListAdapter.js";
34
+ import { createTrueFoundryDraftThreadListAdapter } from "./private/truefoundryDraftThreadListAdapter.js";
32
35
  import { trueFoundryExtras } from "./truefoundryExtras.js";
33
36
  import { createTrueFoundryThreadListAdapter } from "./truefoundryThreadListAdapter.js";
34
37
  import type { UseTrueFoundryAgentRuntimeOptions } from "./types.js";
35
38
  import { resolveTrueFoundryAgentRuntimeOptions } from "./types.js";
36
- import { useDraftAgentSpec } from "./useDraftAgentSpec.js";
39
+ import { useDraftAgentSpec } from "./private/useDraftAgentSpec.js";
37
40
  import { useTrueFoundryAgentMessages } from "./useTrueFoundryAgentMessages.js";
38
41
 
39
42
  function useTrueFoundryAgentRuntimeImpl(
@@ -63,6 +66,9 @@ function useTrueFoundryAgentRuntimeImpl(
63
66
  : undefined,
64
67
  );
65
68
  const sessionId = useAuiState((state) => state.threadListItem.remoteId ?? undefined);
69
+ const isMain = useAuiState(
70
+ (state) => state.threads.mainThreadId === state.threadListItem.id,
71
+ );
66
72
 
67
73
  const draftSpec = useDraftAgentSpec({
68
74
  draftSessionId,
@@ -73,6 +79,20 @@ function useTrueFoundryAgentRuntimeImpl(
73
79
  onError,
74
80
  });
75
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
+
76
96
  const aui = useAui();
77
97
  const initializeSession = useCallback(
78
98
  () => aui.threadListItem().initialize(),
@@ -87,6 +107,9 @@ function useTrueFoundryAgentRuntimeImpl(
87
107
  messages,
88
108
  isRunning,
89
109
  isLoading,
110
+ isLoadingOlderHistory,
111
+ hasOlderHistory,
112
+ loadOlderHistory,
90
113
  sendTurn,
91
114
  cancel,
92
115
  respondToToolApproval,
@@ -94,13 +117,16 @@ function useTrueFoundryAgentRuntimeImpl(
94
117
  resumeRun,
95
118
  editFromTurn,
96
119
  resetFromTurn,
120
+ retryLoad,
97
121
  } = useTrueFoundryAgentMessages({
98
122
  client,
99
123
  sessionId,
124
+ isMain,
100
125
  listEventsConcurrency,
101
126
  onError,
102
127
  initializeSession,
103
128
  draftGateway: agent.mode === "draft" ? gateway : undefined,
129
+ getTurnHeaders: agent.mode === "draft" ? getTurnHeaders : undefined,
104
130
  });
105
131
 
106
132
  if (agent.mode === "draft" && draftSpec.agentSpec != null) {
@@ -175,6 +201,10 @@ function useTrueFoundryAgentRuntimeImpl(
175
201
  resetFromTurn(turnId).catch((error) => {
176
202
  onError?.(error);
177
203
  }),
204
+ reload: retryLoad,
205
+ hasOlderHistory,
206
+ isLoadingOlderHistory,
207
+ loadOlderHistory,
178
208
  draft: draftExtras,
179
209
  }),
180
210
  unstable_enableToolInvocations: true,
@@ -228,34 +258,42 @@ function useTrueFoundryAgentRuntimeImpl(
228
258
  }
229
259
 
230
260
  export function useTrueFoundryAgentRuntime(options: UseTrueFoundryAgentRuntimeOptions) {
231
- const resolved = useMemo(
232
- () => resolveTrueFoundryAgentRuntimeOptions(options),
233
- [options],
234
- );
261
+ // resolveTrueFoundryAgentRuntimeOptions is a cheap pure function; memoizing on the
262
+ // whole `options` object (which callers typically pass as an inline literal) means
263
+ // `resolved` — and everything derived from it — would be recreated on every render,
264
+ // causing `threadListAdapter` to be a new object each render. We compute `resolved`
265
+ // unconditionally and stabilize individual downstream values with primitives/refs.
266
+ const resolved = resolveTrueFoundryAgentRuntimeOptions(options);
235
267
  const { client, agent, gateway } = resolved;
236
268
 
237
269
  const pendingAgentSpecRef = useRef<AgentSpec | undefined>(
238
270
  agent.mode === "draft" ? agent.defaultAgentSpec : undefined,
239
271
  );
240
272
 
273
+ // Use stable primitive deps so that threadListAdapter is not recreated every render
274
+ // when options is an inline object literal (new reference on every parent render).
275
+ const agentMode = agent.mode;
276
+ const namedAgentName = agent.mode === "named" ? agent.agentName : undefined;
241
277
  const threadListAdapter = useMemo(() => {
242
- if (agent.mode === "draft") {
278
+ if (agentMode === "draft") {
243
279
  if (gateway == null) {
244
280
  throw new Error(
245
281
  "Draft agent mode requires a `gateway` TrueFoundryGateway client.",
246
282
  );
247
283
  }
284
+ const draftAgent = agent as Extract<typeof agent, { mode: "draft" }>;
248
285
  return createTrueFoundryDraftThreadListAdapter({
249
286
  gateway,
250
- defaultAgentSpec: agent.defaultAgentSpec,
251
- getAgentSpec: () => pendingAgentSpecRef.current ?? agent.defaultAgentSpec,
287
+ defaultAgentSpec: draftAgent.defaultAgentSpec,
288
+ getAgentSpec: () => pendingAgentSpecRef.current ?? draftAgent.defaultAgentSpec,
252
289
  });
253
290
  }
254
291
  return createTrueFoundryThreadListAdapter({
255
292
  client,
256
- agentName: agent.agentName,
293
+ agentName: namedAgentName!,
257
294
  });
258
- }, [agent, client, gateway]);
295
+ // eslint-disable-next-line react-hooks/exhaustive-deps
296
+ }, [agentMode, namedAgentName, client, gateway]);
259
297
 
260
298
  return useRemoteThreadListRuntime({
261
299
  allowNesting: true,
File without changes