@truefoundry/assistant-ui-runtime 0.1.6 → 0.1.7
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 +3 -2
- package/dist/{chunk-Q2SHKMLM.js → chunk-SQDOTGP2.js} +32 -10
- package/dist/chunk-SQDOTGP2.js.map +1 -0
- package/dist/index.d.ts +19 -23
- package/dist/index.js +256 -162
- package/dist/index.js.map +1 -1
- package/dist/plugins/truefoundry-agent-server-adapter/index.d.ts +3 -6
- package/dist/plugins/truefoundry-agent-server-adapter/index.js +1 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/{types-BfiFf8O1.d.ts → types-DbNsU075.d.ts} +196 -9
- package/package.json +1 -1
- package/src/{private → draft}/agentSpec.ts +14 -17
- package/src/{private → draft}/draftSessionBridge.ts +1 -2
- package/src/{private → draft}/truefoundryDraftThreadListAdapter.test.ts +1 -1
- package/src/{private → draft}/truefoundryDraftThreadListAdapter.ts +2 -1
- package/src/{private → draft}/useDraftAgentSpec.ts +16 -5
- package/src/draftAgentConfig.test.ts +2 -1
- package/src/index.ts +30 -7
- package/src/plugins/truefoundry-agent-server-adapter/index.ts +51 -11
- package/src/plugins/truefoundry-agent-server-adapter/types.ts +6 -4
- package/src/server/index.ts +23 -0
- package/src/server/types.ts +256 -11
- package/src/truefoundryExtras.ts +4 -1
- package/src/truefoundryOwnedSessionsThreadListAdapter.ts +1 -1
- package/src/types.ts +1 -2
- package/src/useTrueFoundryAgentMessages.test.tsx +261 -1
- package/src/useTrueFoundryAgentMessages.ts +284 -176
- package/src/useTrueFoundryAgentRuntime.ts +31 -21
- package/dist/chunk-Q2SHKMLM.js.map +0 -1
- /package/src/{private → draft}/useDraftAgentSpec.test.tsx +0 -0
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
isTfyMcpToolInfo,
|
|
11
11
|
isTfySystemToolInfo,
|
|
12
12
|
isTfyToolInfo
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-SQDOTGP2.js";
|
|
14
14
|
|
|
15
15
|
// src/useTrueFoundryAgentRuntime.ts
|
|
16
16
|
import {
|
|
@@ -2386,7 +2386,7 @@ function repositoryItemsFromMessages(messages) {
|
|
|
2386
2386
|
return items;
|
|
2387
2387
|
}
|
|
2388
2388
|
|
|
2389
|
-
// src/
|
|
2389
|
+
// src/draft/draftSessionBridge.ts
|
|
2390
2390
|
var DRAFT_SESSION_LAST_UPDATED_AT_HEADER = "x-tfy-session-last-updated-at";
|
|
2391
2391
|
function createDraftSessionBridge(server) {
|
|
2392
2392
|
return {
|
|
@@ -2409,7 +2409,7 @@ function createDraftSessionBridge(server) {
|
|
|
2409
2409
|
};
|
|
2410
2410
|
}
|
|
2411
2411
|
|
|
2412
|
-
// src/
|
|
2412
|
+
// src/draft/agentSpec.ts
|
|
2413
2413
|
function mergeAgentSpec(base, update) {
|
|
2414
2414
|
const { model: modelUpdate, ...rest } = update;
|
|
2415
2415
|
const next = {
|
|
@@ -2437,7 +2437,7 @@ function sessionListStartTimestamp() {
|
|
|
2437
2437
|
return start.toISOString();
|
|
2438
2438
|
}
|
|
2439
2439
|
|
|
2440
|
-
// src/
|
|
2440
|
+
// src/draft/truefoundryDraftThreadListAdapter.ts
|
|
2441
2441
|
var THREAD_LIST_PAGE_SIZE = 20;
|
|
2442
2442
|
function createTrueFoundryDraftThreadListAdapter(options) {
|
|
2443
2443
|
const { server, defaultAgentSpec, getAgentSpec } = options;
|
|
@@ -2502,6 +2502,7 @@ var trueFoundryExtras = createRuntimeExtras(
|
|
|
2502
2502
|
var EMPTY_DRAFT_EXTRAS = {
|
|
2503
2503
|
agentSpec: null,
|
|
2504
2504
|
draftSessionId: void 0,
|
|
2505
|
+
isSpecLoading: false,
|
|
2505
2506
|
isSpecSyncing: false,
|
|
2506
2507
|
specError: null,
|
|
2507
2508
|
updateAgentSpec: () => {
|
|
@@ -2597,7 +2598,7 @@ function resolveTrueFoundryAgentRuntimeOptions(options) {
|
|
|
2597
2598
|
};
|
|
2598
2599
|
}
|
|
2599
2600
|
|
|
2600
|
-
// src/
|
|
2601
|
+
// src/draft/useDraftAgentSpec.ts
|
|
2601
2602
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2602
2603
|
var SPEC_SYNC_DEBOUNCE_MS = 400;
|
|
2603
2604
|
function useDraftAgentSpec({
|
|
@@ -2609,6 +2610,7 @@ function useDraftAgentSpec({
|
|
|
2609
2610
|
}) {
|
|
2610
2611
|
const enabled = draftBridge != null;
|
|
2611
2612
|
const [agentSpec, setAgentSpec] = useState(defaultAgentSpec);
|
|
2613
|
+
const [isSpecLoading, setIsSpecLoading] = useState(false);
|
|
2612
2614
|
const [isSpecSyncing, setIsSpecSyncing] = useState(false);
|
|
2613
2615
|
const [specError, setSpecError] = useState(null);
|
|
2614
2616
|
const agentSpecRef = useRef(agentSpec);
|
|
@@ -2649,12 +2651,14 @@ function useDraftAgentSpec({
|
|
|
2649
2651
|
setAgentSpec(defaultAgentSpec);
|
|
2650
2652
|
localDirtyRef.current = false;
|
|
2651
2653
|
setSpecError(null);
|
|
2654
|
+
setIsSpecLoading(false);
|
|
2652
2655
|
return;
|
|
2653
2656
|
}
|
|
2654
2657
|
if (loadedDraftIdRef.current === draftSessionId) {
|
|
2655
2658
|
return;
|
|
2656
2659
|
}
|
|
2657
2660
|
let cancelled = false;
|
|
2661
|
+
setIsSpecLoading(true);
|
|
2658
2662
|
void (async () => {
|
|
2659
2663
|
try {
|
|
2660
2664
|
const loaded = await draftBridge.getDraftAgentSpec(draftSessionId);
|
|
@@ -2666,19 +2670,23 @@ function useDraftAgentSpec({
|
|
|
2666
2670
|
scheduleSpecSyncRef.current?.(draftSessionId, agentSpecRef.current);
|
|
2667
2671
|
localDirtyRef.current = false;
|
|
2668
2672
|
setSpecError(null);
|
|
2673
|
+
setIsSpecLoading(false);
|
|
2669
2674
|
return;
|
|
2670
2675
|
}
|
|
2671
2676
|
setAgentSpec(loaded);
|
|
2672
2677
|
setSpecError(null);
|
|
2678
|
+
setIsSpecLoading(false);
|
|
2673
2679
|
} catch (error) {
|
|
2674
2680
|
if (!cancelled) {
|
|
2675
2681
|
onError?.(error);
|
|
2676
2682
|
setSpecError(error);
|
|
2683
|
+
setIsSpecLoading(false);
|
|
2677
2684
|
}
|
|
2678
2685
|
}
|
|
2679
2686
|
})();
|
|
2680
2687
|
return () => {
|
|
2681
2688
|
cancelled = true;
|
|
2689
|
+
setIsSpecLoading(false);
|
|
2682
2690
|
};
|
|
2683
2691
|
}, [defaultAgentSpec, draftBridge, draftSessionId, enabled, onError]);
|
|
2684
2692
|
const flushSpecSync = useCallback(
|
|
@@ -2781,6 +2789,7 @@ function useDraftAgentSpec({
|
|
|
2781
2789
|
() => ({
|
|
2782
2790
|
agentSpec: enabled ? agentSpec : null,
|
|
2783
2791
|
draftSessionId: enabled ? draftSessionId : void 0,
|
|
2792
|
+
isSpecLoading: enabled ? isSpecLoading : false,
|
|
2784
2793
|
isSpecSyncing: enabled ? isSpecSyncing : false,
|
|
2785
2794
|
specError: enabled ? specError : null,
|
|
2786
2795
|
updateAgentSpec,
|
|
@@ -2790,6 +2799,7 @@ function useDraftAgentSpec({
|
|
|
2790
2799
|
agentSpec,
|
|
2791
2800
|
draftSessionId,
|
|
2792
2801
|
enabled,
|
|
2802
|
+
isSpecLoading,
|
|
2793
2803
|
isSpecSyncing,
|
|
2794
2804
|
specError,
|
|
2795
2805
|
takeTurnHeaderTimestamp,
|
|
@@ -3058,6 +3068,7 @@ function useTrueFoundryAgentMessages({
|
|
|
3058
3068
|
server,
|
|
3059
3069
|
sessionId,
|
|
3060
3070
|
isMain,
|
|
3071
|
+
isInitialSession,
|
|
3061
3072
|
listEventsConcurrency,
|
|
3062
3073
|
onError,
|
|
3063
3074
|
initializeSession,
|
|
@@ -3066,7 +3077,9 @@ function useTrueFoundryAgentMessages({
|
|
|
3066
3077
|
}) {
|
|
3067
3078
|
const [snapshot, setSnapshot] = useState2(createEmptySessionSnapshot);
|
|
3068
3079
|
const [isRunning, setIsRunning] = useState2(false);
|
|
3069
|
-
const [isLoading, setIsLoading] = useState2(
|
|
3080
|
+
const [isLoading, setIsLoading] = useState2(
|
|
3081
|
+
sessionId != null && (isMain !== false || isInitialSession === true)
|
|
3082
|
+
);
|
|
3070
3083
|
const [isLoadingOlderHistory, setIsLoadingOlderHistory] = useState2(false);
|
|
3071
3084
|
const [loadRetryTrigger, setLoadRetryTrigger] = useState2(0);
|
|
3072
3085
|
const snapshotRef = useRef2(snapshot);
|
|
@@ -3087,6 +3100,8 @@ function useTrueFoundryAgentMessages({
|
|
|
3087
3100
|
const loadGenerationRef = useRef2(0);
|
|
3088
3101
|
const streamGenerationRef = useRef2(0);
|
|
3089
3102
|
const lazilyCreatedSessionIdRef = useRef2(void 0);
|
|
3103
|
+
const initialLoadStartedForRef = useRef2(void 0);
|
|
3104
|
+
const skipInitialPromotionLoadForRef = useRef2(void 0);
|
|
3090
3105
|
const projectOptions = useMemo2(
|
|
3091
3106
|
() => ({
|
|
3092
3107
|
getCreatedAt: (messageId, fallback) => {
|
|
@@ -3197,12 +3212,24 @@ function useTrueFoundryAgentMessages({
|
|
|
3197
3212
|
[onError]
|
|
3198
3213
|
);
|
|
3199
3214
|
const load = useCallback2(async () => {
|
|
3215
|
+
void loadRetryTrigger;
|
|
3200
3216
|
if (sessionId == null) {
|
|
3201
3217
|
createdAtByMessageIdRef.current = /* @__PURE__ */ new Map();
|
|
3202
3218
|
setSnapshot(createEmptySessionSnapshot());
|
|
3203
3219
|
return;
|
|
3204
3220
|
}
|
|
3205
|
-
|
|
3221
|
+
const isEarlyInitialLoad = isMain === false && isInitialSession === true && initialLoadStartedForRef.current !== sessionId;
|
|
3222
|
+
if (isMain === false) {
|
|
3223
|
+
if (!isEarlyInitialLoad) return;
|
|
3224
|
+
initialLoadStartedForRef.current = sessionId;
|
|
3225
|
+
skipInitialPromotionLoadForRef.current = sessionId;
|
|
3226
|
+
} else if (isMain === true && skipInitialPromotionLoadForRef.current === sessionId) {
|
|
3227
|
+
skipInitialPromotionLoadForRef.current = void 0;
|
|
3228
|
+
return;
|
|
3229
|
+
}
|
|
3230
|
+
if (isInitialSession === true) {
|
|
3231
|
+
initialLoadStartedForRef.current = sessionId;
|
|
3232
|
+
}
|
|
3206
3233
|
if (lazilyCreatedSessionIdRef.current != null && sessionId !== lazilyCreatedSessionIdRef.current) {
|
|
3207
3234
|
lazilyCreatedSessionIdRef.current = void 0;
|
|
3208
3235
|
}
|
|
@@ -3211,6 +3238,7 @@ function useTrueFoundryAgentMessages({
|
|
|
3211
3238
|
}
|
|
3212
3239
|
const generation = ++loadGenerationRef.current;
|
|
3213
3240
|
++streamGenerationRef.current;
|
|
3241
|
+
setIsRunning(false);
|
|
3214
3242
|
abortControllerRef.current?.abort();
|
|
3215
3243
|
loadOlderInflightRef.current = null;
|
|
3216
3244
|
createdAtByMessageIdRef.current = /* @__PURE__ */ new Map();
|
|
@@ -3257,6 +3285,10 @@ function useTrueFoundryAgentMessages({
|
|
|
3257
3285
|
}
|
|
3258
3286
|
} catch (error) {
|
|
3259
3287
|
if (generation === loadGenerationRef.current) {
|
|
3288
|
+
if (isEarlyInitialLoad) {
|
|
3289
|
+
initialLoadStartedForRef.current = void 0;
|
|
3290
|
+
skipInitialPromotionLoadForRef.current = void 0;
|
|
3291
|
+
}
|
|
3260
3292
|
onErrorRef.current?.(error);
|
|
3261
3293
|
}
|
|
3262
3294
|
throw error;
|
|
@@ -3265,134 +3297,178 @@ function useTrueFoundryAgentMessages({
|
|
|
3265
3297
|
setIsLoading(false);
|
|
3266
3298
|
}
|
|
3267
3299
|
}
|
|
3268
|
-
}, [
|
|
3300
|
+
}, [
|
|
3301
|
+
server,
|
|
3302
|
+
runStream,
|
|
3303
|
+
sessionId,
|
|
3304
|
+
loadRetryTrigger,
|
|
3305
|
+
isMain,
|
|
3306
|
+
isInitialSession
|
|
3307
|
+
]);
|
|
3269
3308
|
useEffect2(() => {
|
|
3270
3309
|
void load().catch(() => void 0);
|
|
3271
3310
|
}, [load]);
|
|
3272
3311
|
const sendTurn = useCallback2(
|
|
3273
3312
|
async (options) => {
|
|
3274
|
-
let
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3313
|
+
let gatewayTurnAccepted = false;
|
|
3314
|
+
let pendingUserWasSet = false;
|
|
3315
|
+
let runStreamStarted = false;
|
|
3316
|
+
let pendingUserTurnId;
|
|
3317
|
+
try {
|
|
3318
|
+
let activeSessionId = sessionId;
|
|
3319
|
+
if (activeSessionId == null) {
|
|
3320
|
+
if (initializeSessionRef.current == null) {
|
|
3321
|
+
throw new Error("Cannot send a turn without an active session.");
|
|
3322
|
+
}
|
|
3323
|
+
const { remoteId } = await initializeSessionRef.current();
|
|
3324
|
+
activeSessionId = remoteId;
|
|
3325
|
+
lazilyCreatedSessionIdRef.current = remoteId;
|
|
3278
3326
|
}
|
|
3279
|
-
const
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
}
|
|
3283
|
-
const conversationSessionId = await resolveActiveSessionId(
|
|
3284
|
-
activeSessionId,
|
|
3285
|
-
resolveConversationSessionIdRef.current
|
|
3286
|
-
);
|
|
3287
|
-
const turnHeaders = await getTurnHeadersRef.current?.();
|
|
3288
|
-
const streamHeaders = turnHeaders != null ? { headers: turnHeaders } : {};
|
|
3289
|
-
const isContinuation = "inputs" in options || "resumeMcpAuth" in options && options.resumeMcpAuth === true;
|
|
3290
|
-
const continuationTurnId = snapshotRef.current.activeStream?.turnId;
|
|
3291
|
-
const turnId = isContinuation && continuationTurnId != null ? continuationTurnId : generateId();
|
|
3292
|
-
const isFirstTurnInSession = "userMessage" in options && options.previousTurnId === void 0 && snapshotRef.current.turns.length === 0 && snapshotRef.current.pendingUser == null && snapshotRef.current.activeStream == null;
|
|
3293
|
-
const turnIdRef = { current: turnId };
|
|
3294
|
-
if ("inputs" in options) {
|
|
3295
|
-
applyUserToolResponsesToFold(
|
|
3296
|
-
snapshotRef.current.fold,
|
|
3297
|
-
options.inputs
|
|
3298
|
-
);
|
|
3299
|
-
}
|
|
3300
|
-
const branchBase = "userMessage" in options ? options.branchFromSnapshot : void 0;
|
|
3301
|
-
let groupRootBaseline;
|
|
3302
|
-
if (branchBase != null && "userMessage" in options) {
|
|
3303
|
-
const rootBucket = branchBase.fold.threads.get(ROOT_THREAD_ID);
|
|
3304
|
-
groupRootBaseline = [...rootBucket?.modelMessageIds ?? []];
|
|
3305
|
-
const nextSnapshot = replaceSessionSnapshot(branchBase, {
|
|
3306
|
-
pendingUser: {
|
|
3307
|
-
turnId,
|
|
3308
|
-
content: options.userMessage,
|
|
3309
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
3310
|
-
},
|
|
3311
|
-
activeStream: void 0,
|
|
3312
|
-
groupRootBaseline
|
|
3313
|
-
});
|
|
3314
|
-
snapshotRef.current = nextSnapshot;
|
|
3315
|
-
setSnapshot(nextSnapshot);
|
|
3316
|
-
} else {
|
|
3317
|
-
setSnapshot(
|
|
3318
|
-
(prev) => commitActiveStream(
|
|
3319
|
-
prev,
|
|
3320
|
-
"inputs" in options ? options.inputs : void 0
|
|
3321
|
-
)
|
|
3327
|
+
const conversationSessionId = await resolveActiveSessionId(
|
|
3328
|
+
activeSessionId,
|
|
3329
|
+
resolveConversationSessionIdRef.current
|
|
3322
3330
|
);
|
|
3323
|
-
|
|
3324
|
-
|
|
3331
|
+
const turnHeaders = await getTurnHeadersRef.current?.();
|
|
3332
|
+
const streamHeaders = turnHeaders != null ? { headers: turnHeaders } : {};
|
|
3333
|
+
const isContinuation = "inputs" in options || "resumeMcpAuth" in options && options.resumeMcpAuth === true;
|
|
3334
|
+
const continuationTurnId = snapshotRef.current.activeStream?.turnId;
|
|
3335
|
+
const turnId = isContinuation && continuationTurnId != null ? continuationTurnId : generateId();
|
|
3336
|
+
const isFirstTurnInSession = "userMessage" in options && options.previousTurnId === void 0 && snapshotRef.current.turns.length === 0 && snapshotRef.current.pendingUser == null && snapshotRef.current.activeStream == null;
|
|
3337
|
+
const turnIdRef = { current: turnId };
|
|
3338
|
+
if ("inputs" in options) {
|
|
3339
|
+
applyUserToolResponsesToFold(
|
|
3340
|
+
snapshotRef.current.fold,
|
|
3341
|
+
options.inputs
|
|
3342
|
+
);
|
|
3343
|
+
}
|
|
3344
|
+
const branchBase = "userMessage" in options ? options.branchFromSnapshot : void 0;
|
|
3345
|
+
let groupRootBaseline;
|
|
3346
|
+
if (branchBase != null && "userMessage" in options) {
|
|
3347
|
+
const rootBucket = branchBase.fold.threads.get(ROOT_THREAD_ID);
|
|
3325
3348
|
groupRootBaseline = [...rootBucket?.modelMessageIds ?? []];
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
groupRootBaseline
|
|
3335
|
-
});
|
|
3336
|
-
snapshotRef.current = next;
|
|
3337
|
-
return next;
|
|
3349
|
+
const nextSnapshot = replaceSessionSnapshot(branchBase, {
|
|
3350
|
+
pendingUser: {
|
|
3351
|
+
turnId,
|
|
3352
|
+
content: options.userMessage,
|
|
3353
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
3354
|
+
},
|
|
3355
|
+
activeStream: void 0,
|
|
3356
|
+
groupRootBaseline
|
|
3338
3357
|
});
|
|
3358
|
+
snapshotRef.current = nextSnapshot;
|
|
3359
|
+
setSnapshot(nextSnapshot);
|
|
3360
|
+
pendingUserWasSet = true;
|
|
3361
|
+
pendingUserTurnId = turnId;
|
|
3339
3362
|
} else {
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3363
|
+
setSnapshot(
|
|
3364
|
+
(prev) => commitActiveStream(
|
|
3365
|
+
prev,
|
|
3366
|
+
"inputs" in options ? options.inputs : void 0
|
|
3367
|
+
)
|
|
3368
|
+
);
|
|
3369
|
+
if ("userMessage" in options) {
|
|
3370
|
+
const rootBucket = snapshotRef.current.fold.threads.get(ROOT_THREAD_ID);
|
|
3371
|
+
groupRootBaseline = [...rootBucket?.modelMessageIds ?? []];
|
|
3372
|
+
setSnapshot((prev) => {
|
|
3373
|
+
const next = replaceSessionSnapshot(prev, {
|
|
3374
|
+
pendingUser: {
|
|
3375
|
+
turnId,
|
|
3376
|
+
content: options.userMessage,
|
|
3377
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
3378
|
+
},
|
|
3379
|
+
activeStream: void 0,
|
|
3380
|
+
groupRootBaseline
|
|
3381
|
+
});
|
|
3382
|
+
snapshotRef.current = next;
|
|
3383
|
+
return next;
|
|
3384
|
+
});
|
|
3385
|
+
pendingUserWasSet = true;
|
|
3386
|
+
pendingUserTurnId = turnId;
|
|
3387
|
+
} else {
|
|
3388
|
+
groupRootBaseline = snapshotRef.current.groupRootBaseline ?? computeGroupRootBaseline(snapshotRef.current.turns);
|
|
3354
3389
|
}
|
|
3355
|
-
|
|
3390
|
+
}
|
|
3391
|
+
runStreamStarted = true;
|
|
3392
|
+
await runStream(
|
|
3393
|
+
(signal) => {
|
|
3394
|
+
if ("inputs" in options) {
|
|
3395
|
+
return streamTurnContent(
|
|
3396
|
+
server,
|
|
3397
|
+
conversationSessionId,
|
|
3398
|
+
snapshotRef.current.fold,
|
|
3399
|
+
{ inputs: options.inputs, ...streamHeaders },
|
|
3400
|
+
signal,
|
|
3401
|
+
groupRootBaseline
|
|
3402
|
+
);
|
|
3403
|
+
}
|
|
3404
|
+
if ("resumeMcpAuth" in options) {
|
|
3405
|
+
return streamTurnContent(
|
|
3406
|
+
server,
|
|
3407
|
+
conversationSessionId,
|
|
3408
|
+
snapshotRef.current.fold,
|
|
3409
|
+
{ resumeMcpAuth: true, ...streamHeaders },
|
|
3410
|
+
signal,
|
|
3411
|
+
groupRootBaseline
|
|
3412
|
+
);
|
|
3413
|
+
}
|
|
3356
3414
|
return streamTurnContent(
|
|
3357
3415
|
server,
|
|
3358
3416
|
conversationSessionId,
|
|
3359
3417
|
snapshotRef.current.fold,
|
|
3360
|
-
{
|
|
3418
|
+
{
|
|
3419
|
+
userMessage: options.userMessage,
|
|
3420
|
+
...options.previousTurnId !== void 0 ? { previousTurnId: options.previousTurnId ?? "none" } : isFirstTurnInSession ? { previousTurnId: "none" } : {},
|
|
3421
|
+
...streamHeaders
|
|
3422
|
+
},
|
|
3361
3423
|
signal,
|
|
3362
|
-
groupRootBaseline
|
|
3424
|
+
groupRootBaseline,
|
|
3425
|
+
// Rename the optimistic local ID to the gateway turn ID
|
|
3426
|
+
// so that edit/retry can resolve the turn via the gateway.
|
|
3427
|
+
(gatewayTurnId) => {
|
|
3428
|
+
const oldId = turnIdRef.current;
|
|
3429
|
+
gatewayTurnAccepted = true;
|
|
3430
|
+
if (gatewayTurnId === oldId) return;
|
|
3431
|
+
turnIdRef.current = gatewayTurnId;
|
|
3432
|
+
const renamePendingUser = (prev) => {
|
|
3433
|
+
if (prev.pendingUser?.turnId !== oldId) return prev;
|
|
3434
|
+
return replaceSessionSnapshot(prev, {
|
|
3435
|
+
pendingUser: { ...prev.pendingUser, turnId: gatewayTurnId }
|
|
3436
|
+
});
|
|
3437
|
+
};
|
|
3438
|
+
snapshotRef.current = renamePendingUser(snapshotRef.current);
|
|
3439
|
+
setSnapshot(renamePendingUser);
|
|
3440
|
+
}
|
|
3363
3441
|
);
|
|
3442
|
+
},
|
|
3443
|
+
turnIdRef,
|
|
3444
|
+
isContinuation
|
|
3445
|
+
);
|
|
3446
|
+
} catch (error) {
|
|
3447
|
+
if ("userMessage" in options && !gatewayTurnAccepted) {
|
|
3448
|
+
const branchRollbackSnapshot = options.branchRollbackSnapshot;
|
|
3449
|
+
const canRestoreBranch = branchRollbackSnapshot != null && (snapshotRef.current === options.branchFromSnapshot || snapshotRef.current.pendingUser?.turnId === pendingUserTurnId);
|
|
3450
|
+
if (canRestoreBranch) {
|
|
3451
|
+
snapshotRef.current = branchRollbackSnapshot;
|
|
3452
|
+
setSnapshot(branchRollbackSnapshot);
|
|
3453
|
+
} else if (pendingUserWasSet) {
|
|
3454
|
+
const clearPendingUser = (previous) => {
|
|
3455
|
+
if (previous.pendingUser?.turnId !== pendingUserTurnId) {
|
|
3456
|
+
return previous;
|
|
3457
|
+
}
|
|
3458
|
+
return replaceSessionSnapshot(previous, {
|
|
3459
|
+
pendingUser: void 0
|
|
3460
|
+
});
|
|
3461
|
+
};
|
|
3462
|
+
snapshotRef.current = clearPendingUser(snapshotRef.current);
|
|
3463
|
+
setSnapshot(clearPendingUser);
|
|
3364
3464
|
}
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
...streamHeaders
|
|
3373
|
-
},
|
|
3374
|
-
signal,
|
|
3375
|
-
groupRootBaseline,
|
|
3376
|
-
// Rename the optimistic local ID to the gateway turn ID
|
|
3377
|
-
// so that edit/retry can resolve the turn via the gateway.
|
|
3378
|
-
(gatewayTurnId) => {
|
|
3379
|
-
const oldId = turnIdRef.current;
|
|
3380
|
-
if (gatewayTurnId === oldId) return;
|
|
3381
|
-
turnIdRef.current = gatewayTurnId;
|
|
3382
|
-
const renamePendingUser = (prev) => {
|
|
3383
|
-
if (prev.pendingUser?.turnId !== oldId) return prev;
|
|
3384
|
-
return replaceSessionSnapshot(prev, {
|
|
3385
|
-
pendingUser: { ...prev.pendingUser, turnId: gatewayTurnId }
|
|
3386
|
-
});
|
|
3387
|
-
};
|
|
3388
|
-
snapshotRef.current = renamePendingUser(snapshotRef.current);
|
|
3389
|
-
setSnapshot(renamePendingUser);
|
|
3390
|
-
}
|
|
3391
|
-
);
|
|
3392
|
-
},
|
|
3393
|
-
turnIdRef,
|
|
3394
|
-
isContinuation
|
|
3395
|
-
);
|
|
3465
|
+
options.onPreTurnFailure?.();
|
|
3466
|
+
}
|
|
3467
|
+
if (!runStreamStarted) {
|
|
3468
|
+
onErrorRef.current?.(error);
|
|
3469
|
+
}
|
|
3470
|
+
throw error;
|
|
3471
|
+
}
|
|
3396
3472
|
},
|
|
3397
3473
|
[server, runStream, sessionId]
|
|
3398
3474
|
);
|
|
@@ -3422,7 +3498,7 @@ function useTrueFoundryAgentMessages({
|
|
|
3422
3498
|
}
|
|
3423
3499
|
const inputs = collectRequiredActionInputs(paused);
|
|
3424
3500
|
if (inputs.length > 0) {
|
|
3425
|
-
void sendTurn({ inputs }).catch((
|
|
3501
|
+
void sendTurn({ inputs }).catch(() => void 0);
|
|
3426
3502
|
}
|
|
3427
3503
|
},
|
|
3428
3504
|
[projectOptions, sendTurn]
|
|
@@ -3483,35 +3559,44 @@ function useTrueFoundryAgentMessages({
|
|
|
3483
3559
|
}, [runStream, server]);
|
|
3484
3560
|
const branchFromTurn = useCallback2(
|
|
3485
3561
|
async (turnId, userMessage) => {
|
|
3486
|
-
let
|
|
3487
|
-
|
|
3488
|
-
|
|
3562
|
+
let committed;
|
|
3563
|
+
let previousTurnId;
|
|
3564
|
+
let rewound;
|
|
3565
|
+
try {
|
|
3566
|
+
let activeSessionId = sessionId;
|
|
3567
|
+
if (activeSessionId == null) {
|
|
3568
|
+
throw new Error("Cannot branch from a turn without an active session.");
|
|
3569
|
+
}
|
|
3570
|
+
committed = commitActiveStream(snapshotRef.current);
|
|
3571
|
+
setSnapshot(committed);
|
|
3572
|
+
await cancel();
|
|
3573
|
+
const conversationSessionId = await resolveActiveSessionId(
|
|
3574
|
+
activeSessionId,
|
|
3575
|
+
resolveConversationSessionIdRef.current
|
|
3576
|
+
);
|
|
3577
|
+
previousTurnId = await resolveGatewayBranchPreviousTurnIdForTurn(
|
|
3578
|
+
server,
|
|
3579
|
+
conversationSessionId,
|
|
3580
|
+
turnId
|
|
3581
|
+
);
|
|
3582
|
+
rewound = await buildSnapshotBeforeTurn(
|
|
3583
|
+
server,
|
|
3584
|
+
conversationSessionId,
|
|
3585
|
+
turnId,
|
|
3586
|
+
listEventsConcurrency
|
|
3587
|
+
);
|
|
3588
|
+
createdAtByMessageIdRef.current = /* @__PURE__ */ new Map();
|
|
3589
|
+
snapshotRef.current = rewound;
|
|
3590
|
+
setSnapshot(rewound);
|
|
3591
|
+
} catch (error) {
|
|
3592
|
+
onErrorRef.current?.(error);
|
|
3593
|
+
throw error;
|
|
3489
3594
|
}
|
|
3490
|
-
const committed = commitActiveStream(snapshotRef.current);
|
|
3491
|
-
setSnapshot(committed);
|
|
3492
|
-
await cancel();
|
|
3493
|
-
const conversationSessionId = await resolveActiveSessionId(
|
|
3494
|
-
activeSessionId,
|
|
3495
|
-
resolveConversationSessionIdRef.current
|
|
3496
|
-
);
|
|
3497
|
-
const previousTurnId = await resolveGatewayBranchPreviousTurnIdForTurn(
|
|
3498
|
-
server,
|
|
3499
|
-
conversationSessionId,
|
|
3500
|
-
turnId
|
|
3501
|
-
);
|
|
3502
|
-
const rewound = await buildSnapshotBeforeTurn(
|
|
3503
|
-
server,
|
|
3504
|
-
conversationSessionId,
|
|
3505
|
-
turnId,
|
|
3506
|
-
listEventsConcurrency
|
|
3507
|
-
);
|
|
3508
|
-
createdAtByMessageIdRef.current = /* @__PURE__ */ new Map();
|
|
3509
|
-
snapshotRef.current = rewound;
|
|
3510
|
-
setSnapshot(rewound);
|
|
3511
3595
|
await sendTurn({
|
|
3512
3596
|
userMessage,
|
|
3513
3597
|
previousTurnId,
|
|
3514
|
-
branchFromSnapshot: rewound
|
|
3598
|
+
branchFromSnapshot: rewound,
|
|
3599
|
+
branchRollbackSnapshot: committed
|
|
3515
3600
|
});
|
|
3516
3601
|
},
|
|
3517
3602
|
[
|
|
@@ -3527,7 +3612,9 @@ function useTrueFoundryAgentMessages({
|
|
|
3527
3612
|
const committed = commitActiveStream(snapshotRef.current);
|
|
3528
3613
|
const originalInput = resolveTurnInput(committed, turnId);
|
|
3529
3614
|
if (originalInput == null) {
|
|
3530
|
-
|
|
3615
|
+
const error = new Error(`Turn ${turnId} not found in session snapshot`);
|
|
3616
|
+
onErrorRef.current?.(error);
|
|
3617
|
+
throw error;
|
|
3531
3618
|
}
|
|
3532
3619
|
const userMessage = extractTurnUserMessageContent(originalInput);
|
|
3533
3620
|
await branchFromTurn(turnId, userMessage);
|
|
@@ -3539,7 +3626,9 @@ function useTrueFoundryAgentMessages({
|
|
|
3539
3626
|
const committed = commitActiveStream(snapshotRef.current);
|
|
3540
3627
|
const originalInput = resolveTurnInput(committed, turnId);
|
|
3541
3628
|
if (originalInput == null) {
|
|
3542
|
-
|
|
3629
|
+
const error = new Error(`Turn ${turnId} not found in session snapshot`);
|
|
3630
|
+
onErrorRef.current?.(error);
|
|
3631
|
+
throw error;
|
|
3543
3632
|
}
|
|
3544
3633
|
const userMessage = buildEditedUserMessageContent(
|
|
3545
3634
|
editedText,
|
|
@@ -3638,6 +3727,7 @@ function useTrueFoundryAgentRuntimeImpl(options, pendingAgentSpecRef) {
|
|
|
3638
3727
|
const isMain = useAuiState(
|
|
3639
3728
|
(state) => state.threads.mainThreadId === state.threadListItem.id
|
|
3640
3729
|
);
|
|
3730
|
+
const isInitialSession = sessionId != null && sessionId === options.initialSessionId;
|
|
3641
3731
|
const draftSpec = useDraftAgentSpec({
|
|
3642
3732
|
draftSessionId,
|
|
3643
3733
|
draftBridge: draftBridgeRef.current,
|
|
@@ -3683,6 +3773,7 @@ function useTrueFoundryAgentRuntimeImpl(options, pendingAgentSpecRef) {
|
|
|
3683
3773
|
server,
|
|
3684
3774
|
sessionId,
|
|
3685
3775
|
isMain,
|
|
3776
|
+
isInitialSession,
|
|
3686
3777
|
listEventsConcurrency,
|
|
3687
3778
|
onError,
|
|
3688
3779
|
initializeSession,
|
|
@@ -3708,20 +3799,16 @@ function useTrueFoundryAgentRuntimeImpl(options, pendingAgentSpecRef) {
|
|
|
3708
3799
|
const downloadSandboxFile = useCallback3(
|
|
3709
3800
|
async (path) => {
|
|
3710
3801
|
if (server.downloadSandboxFile == null) {
|
|
3711
|
-
|
|
3802
|
+
throw new Error(
|
|
3712
3803
|
"Downloading a sandbox file requires AgentChatServer.downloadSandboxFile."
|
|
3713
3804
|
);
|
|
3714
|
-
onError?.(error);
|
|
3715
|
-
throw error;
|
|
3716
3805
|
}
|
|
3717
3806
|
if (sandboxId == null) {
|
|
3718
|
-
|
|
3719
|
-
onError?.(error);
|
|
3720
|
-
throw error;
|
|
3807
|
+
throw new Error("No sandbox is available yet for this session.");
|
|
3721
3808
|
}
|
|
3722
3809
|
return await server.downloadSandboxFile(sandboxId, { path });
|
|
3723
3810
|
},
|
|
3724
|
-
[server, sandboxId
|
|
3811
|
+
[server, sandboxId]
|
|
3725
3812
|
);
|
|
3726
3813
|
const draftExtras = useMemo3(() => {
|
|
3727
3814
|
if (agent.mode !== "draft") {
|
|
@@ -3730,6 +3817,7 @@ function useTrueFoundryAgentRuntimeImpl(options, pendingAgentSpecRef) {
|
|
|
3730
3817
|
return {
|
|
3731
3818
|
agentSpec: draftSpec.agentSpec,
|
|
3732
3819
|
draftSessionId: draftSpec.draftSessionId,
|
|
3820
|
+
isSpecLoading: draftSpec.isSpecLoading,
|
|
3733
3821
|
isSpecSyncing: draftSpec.isSpecSyncing,
|
|
3734
3822
|
specError: draftSpec.specError,
|
|
3735
3823
|
updateAgentSpec: draftSpec.updateAgentSpec
|
|
@@ -3750,9 +3838,8 @@ function useTrueFoundryAgentRuntimeImpl(options, pendingAgentSpecRef) {
|
|
|
3750
3838
|
resumeMcpAuth,
|
|
3751
3839
|
downloadSandboxFile,
|
|
3752
3840
|
cancel,
|
|
3753
|
-
resetFromTurn
|
|
3754
|
-
|
|
3755
|
-
}),
|
|
3841
|
+
// resetFromTurn/branchFromTurn/sendTurn already report via onError.
|
|
3842
|
+
resetFromTurn: (turnId) => resetFromTurn(turnId).catch(() => void 0),
|
|
3756
3843
|
reload: retryLoad,
|
|
3757
3844
|
hasOlderHistory,
|
|
3758
3845
|
isLoadingOlderHistory,
|
|
@@ -3777,7 +3864,19 @@ function useTrueFoundryAgentRuntimeImpl(options, pendingAgentSpecRef) {
|
|
|
3777
3864
|
await sendTurn({ resumeMcpAuth: true });
|
|
3778
3865
|
return;
|
|
3779
3866
|
}
|
|
3780
|
-
|
|
3867
|
+
const userMessage = buildUserMessageContent(message);
|
|
3868
|
+
await sendTurn({
|
|
3869
|
+
userMessage,
|
|
3870
|
+
// The composer clears before onNew runs. Restore its text only when
|
|
3871
|
+
// the turn failed before turn.created registered it in the backend.
|
|
3872
|
+
onPreTurnFailure: () => {
|
|
3873
|
+
const text = userMessageContentToText(userMessage);
|
|
3874
|
+
const composer = aui.thread().composer();
|
|
3875
|
+
if (text && !composer.getState().text.trim()) {
|
|
3876
|
+
composer.setText(text);
|
|
3877
|
+
}
|
|
3878
|
+
}
|
|
3879
|
+
});
|
|
3781
3880
|
},
|
|
3782
3881
|
onCancel: async () => {
|
|
3783
3882
|
await cancel();
|
|
@@ -3795,12 +3894,7 @@ function useTrueFoundryAgentRuntimeImpl(options, pendingAgentSpecRef) {
|
|
|
3795
3894
|
}
|
|
3796
3895
|
const turnId = parseTurnIdFromMessageId(sourceId);
|
|
3797
3896
|
const editedText = extractEditedText(message);
|
|
3798
|
-
|
|
3799
|
-
await editFromTurn(turnId, editedText);
|
|
3800
|
-
} catch (error) {
|
|
3801
|
-
onError?.(error);
|
|
3802
|
-
throw error;
|
|
3803
|
-
}
|
|
3897
|
+
await editFromTurn(turnId, editedText);
|
|
3804
3898
|
}
|
|
3805
3899
|
});
|
|
3806
3900
|
}
|