@agents24/react 0.2.26 → 0.2.28
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/compatibility.json +3 -3
- package/dist/assistant-preview.d.ts +18 -0
- package/dist/index.js +113 -7
- package/dist/index.js.map +1 -1
- package/dist/model.d.ts +10 -0
- package/dist/state.d.ts +2 -0
- package/package.json +2 -2
package/compatibility.json
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Ephemeral display only: these fragments never enter stored messages or replay cursors. */
|
|
2
|
+
import type { AssistantPreview } from "@agents24/client/protocol";
|
|
3
|
+
import type { RuntimeEvent } from "@agents24/client";
|
|
4
|
+
import type { ChatMessage } from "./model";
|
|
5
|
+
export type AssistantPreviewState = {
|
|
6
|
+
runId: string;
|
|
7
|
+
key: string;
|
|
8
|
+
segmentId: string;
|
|
9
|
+
committed: number;
|
|
10
|
+
dispatchId: string;
|
|
11
|
+
generation: number;
|
|
12
|
+
chunks: Record<number, string>;
|
|
13
|
+
retired: string[];
|
|
14
|
+
};
|
|
15
|
+
export declare function acceptAssistantPreview(current: AssistantPreviewState | null | undefined, frame: AssistantPreview | null): AssistantPreviewState | null;
|
|
16
|
+
export declare function commitAssistantPreview(current: AssistantPreviewState | null | undefined, event: RuntimeEvent): AssistantPreviewState | null;
|
|
17
|
+
export declare function previewText(state: AssistantPreviewState | null | undefined): string;
|
|
18
|
+
export declare function messagesWithAssistantPreview(messages: ChatMessage[], state: AssistantPreviewState | null | undefined): ChatMessage[];
|
package/dist/index.js
CHANGED
|
@@ -637,6 +637,79 @@ function createRuntimeEventFrameBuffer(apply, scheduleFrame) {
|
|
|
637
637
|
};
|
|
638
638
|
}
|
|
639
639
|
|
|
640
|
+
// src/assistant-preview.ts
|
|
641
|
+
var keyOf = (frame) => `${frame.dispatch_id}:${frame.generation}:${frame.segment_id}`;
|
|
642
|
+
function acceptAssistantPreview(current, frame) {
|
|
643
|
+
if (!frame) return current ? { ...current, chunks: {} } : null;
|
|
644
|
+
const key = keyOf(frame);
|
|
645
|
+
if (current?.runId === frame.run_id && current.dispatchId === frame.dispatch_id && current.generation > frame.generation) return current;
|
|
646
|
+
if (current?.runId === frame.run_id && current.retired.includes(key)) return current;
|
|
647
|
+
const state = current?.runId === frame.run_id && current.key === key ? current : {
|
|
648
|
+
runId: frame.run_id,
|
|
649
|
+
dispatchId: frame.dispatch_id,
|
|
650
|
+
generation: frame.generation,
|
|
651
|
+
key,
|
|
652
|
+
segmentId: frame.segment_id,
|
|
653
|
+
committed: -1,
|
|
654
|
+
chunks: {},
|
|
655
|
+
retired: current?.runId === frame.run_id ? [...current.retired, current.key].slice(-8) : []
|
|
656
|
+
};
|
|
657
|
+
if (frame.chunk_index <= state.committed || Object.keys(state.chunks).length >= 32 || Object.values(state.chunks).reduce((size, text2) => size + text2.length, 0) + frame.text.length > 65536) return state;
|
|
658
|
+
return { ...state, chunks: { ...state.chunks, [frame.chunk_index]: frame.text } };
|
|
659
|
+
}
|
|
660
|
+
function commitAssistantPreview(current, event) {
|
|
661
|
+
if (event.event === "run.accepted" && current && current.runId !== event.run_id) return null;
|
|
662
|
+
if (current && current.runId !== event.run_id) return current;
|
|
663
|
+
if (["assistant.completed", "run.completed", "run.failed", "run.cancelled", "run.waiting", "run.interrupted", "run.paused", "run.snapshot"].includes(event.event)) return null;
|
|
664
|
+
const ack = event.payload.preview_ack;
|
|
665
|
+
if (event.event !== "assistant.delta" || !ack || typeof ack.dispatch_id !== "string" || typeof ack.segment_id !== "string" || !Number.isSafeInteger(ack.generation) || !Number.isSafeInteger(ack.chunk_index)) return current || null;
|
|
666
|
+
const key = keyOf(ack);
|
|
667
|
+
if (current && current.key !== key) return current;
|
|
668
|
+
const committed = Math.max(current?.committed ?? -1, ack.chunk_index);
|
|
669
|
+
return {
|
|
670
|
+
runId: event.run_id,
|
|
671
|
+
dispatchId: ack.dispatch_id,
|
|
672
|
+
generation: ack.generation,
|
|
673
|
+
key,
|
|
674
|
+
segmentId: ack.segment_id,
|
|
675
|
+
committed,
|
|
676
|
+
retired: current?.retired || [],
|
|
677
|
+
chunks: Object.fromEntries(Object.entries(current?.chunks || {}).filter(([index]) => Number(index) > committed))
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
function previewText(state) {
|
|
681
|
+
if (!state) return "";
|
|
682
|
+
let text2 = "";
|
|
683
|
+
for (let index = state.committed + 1; Object.hasOwn(state.chunks, index); index++) text2 += state.chunks[index];
|
|
684
|
+
return text2;
|
|
685
|
+
}
|
|
686
|
+
function messagesWithAssistantPreview(messages, state) {
|
|
687
|
+
const tail = previewText(state);
|
|
688
|
+
if (!state || !tail) return messages;
|
|
689
|
+
let index = -1;
|
|
690
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
691
|
+
if (messages[i].role === "assistant" && messages[i].runId === state.runId && !messages[i].isFinal) {
|
|
692
|
+
index = i;
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
const base = index < 0 ? {
|
|
697
|
+
id: `preview-${state.runId}`,
|
|
698
|
+
role: "assistant",
|
|
699
|
+
runId: state.runId,
|
|
700
|
+
content: "",
|
|
701
|
+
parts: [],
|
|
702
|
+
createdAt: /* @__PURE__ */ new Date(0),
|
|
703
|
+
isFinal: false
|
|
704
|
+
} : messages[index];
|
|
705
|
+
const parts = [...base.parts];
|
|
706
|
+
const last = parts[parts.length - 1];
|
|
707
|
+
if (last?.type === "text") parts[parts.length - 1] = { ...last, text: last.text + tail };
|
|
708
|
+
else parts.push({ id: `preview-${state.segmentId}`, type: "text", kind: "text", text: tail });
|
|
709
|
+
const rendered = { ...base, content: base.content + tail, parts };
|
|
710
|
+
return index < 0 ? [...messages, rendered] : messages.map((message, i) => i === index ? rendered : message);
|
|
711
|
+
}
|
|
712
|
+
|
|
640
713
|
// src/hitl-resolution.ts
|
|
641
714
|
function applyHitlResolution(messages, receipt) {
|
|
642
715
|
return messages.map((message) => message.runId !== receipt.run_id ? message : {
|
|
@@ -856,11 +929,11 @@ function reconcileModelTurnParts(parts, event) {
|
|
|
856
929
|
const terminal = eventName === "run.completed" || eventName === "run.failed" || eventName === "run.cancelled";
|
|
857
930
|
const modelTurnId = typeof payload.model_turn_id === "string" ? payload.model_turn_id : null;
|
|
858
931
|
let next = parts;
|
|
859
|
-
if (
|
|
932
|
+
if (["run.snapshot", "run.waiting", "run.interrupted", "run.paused", "assistant.completed"].includes(eventName) || terminal) {
|
|
860
933
|
next = next.filter((part) => part.kind !== "model-turn");
|
|
861
934
|
} else if (eventName === "assistant.delta" && typeof payload.content === "string" && payload.content) {
|
|
862
935
|
next = next.filter((part) => part.kind !== "model-turn" || part.turnKind !== "agent");
|
|
863
|
-
} else if (eventName
|
|
936
|
+
} else if (eventName === "tool.started") {
|
|
864
937
|
next = next.filter((part) => part.kind !== "model-turn" || part.turnKind !== "agent");
|
|
865
938
|
} else if ((eventName === "model.turn.failed" || eventName === "model.turn.completed" && payload.outcome !== "tool_calls") && modelTurnId) {
|
|
866
939
|
next = next.filter((part) => part.kind !== "model-turn" || part.modelTurnId !== modelTurnId);
|
|
@@ -869,6 +942,11 @@ function reconcileModelTurnParts(parts, event) {
|
|
|
869
942
|
return active ? upsertResponseBlockParts(next, [active]) : next;
|
|
870
943
|
}
|
|
871
944
|
function applyRuntimeEvent(state, event) {
|
|
945
|
+
const next = applyCanonicalRuntimeEvent(state, event);
|
|
946
|
+
if (!state.assistantPreview && !event.payload.preview_ack) return next;
|
|
947
|
+
return { ...next, assistantPreview: commitAssistantPreview(state.assistantPreview, event) };
|
|
948
|
+
}
|
|
949
|
+
function applyCanonicalRuntimeEvent(state, event) {
|
|
872
950
|
if (state.activeRunId && event.run_id && event.run_id !== state.activeRunId) {
|
|
873
951
|
const previous = state.messages.find((message) => message.role === "assistant" && message.runId === event.run_id);
|
|
874
952
|
if (event.event !== "run.accepted") {
|
|
@@ -906,12 +984,24 @@ function applyRuntimeEvent(state, event) {
|
|
|
906
984
|
const distinctSegment = Boolean(segmentId && segmentId !== `${runId}:0`);
|
|
907
985
|
const messageId = distinctSegment ? `${runId || "run"}-assistant-${segmentId}` : state.streamingMessageId || `${runId || "run"}-assistant`;
|
|
908
986
|
const terminal = eventName === "run.completed" || eventName === "run.failed" || eventName === "run.cancelled";
|
|
909
|
-
const
|
|
910
|
-
const
|
|
987
|
+
const snapshotStatus = eventName === "run.snapshot" ? event.payload?.status : null;
|
|
988
|
+
const paused = eventName === "run.paused" || snapshotStatus === "paused" || projection.parts.some((part) => part.kind === "hitl" && part.status === "pending");
|
|
989
|
+
const retainedSuspension = !eventName.startsWith("run.") && !["model.turn.started", "assistant.delta", "tool.started"].includes(eventName) && (state.runState === "waiting" || state.runState === "interrupted") ? state.runState : null;
|
|
990
|
+
const suspension = eventName === "run.waiting" || snapshotStatus === "waiting" ? "waiting" : eventName === "run.interrupted" || snapshotStatus === "interrupted" ? "interrupted" : retainedSuspension;
|
|
911
991
|
const cancelling = eventName === "run.cancelling" || String(event.payload?.status || "") === "cancelling";
|
|
912
992
|
const existing = state.messages.find((message) => message.role === "assistant" && message.id === messageId) || (!distinctSegment ? state.messages.find((message) => message.role === "assistant" && Boolean(runId && message.runId === runId)) : void 0);
|
|
913
993
|
const sealedAfterTerminal = responseCompleted && !state.activeRunId && ["completed", "failed", "cancelled"].includes(state.runState) && existing?.runId === runId;
|
|
914
994
|
const payload = event.payload || {};
|
|
995
|
+
const wait = payload.capacity_wait;
|
|
996
|
+
const capacityWait = wait && typeof wait.reason === "string" ? {
|
|
997
|
+
reason: wait.reason,
|
|
998
|
+
...typeof wait.retry_at === "string" ? { retryAt: wait.retry_at } : {},
|
|
999
|
+
...typeof wait.deadline === "string" ? { deadline: wait.deadline } : {}
|
|
1000
|
+
} : state.runState === "waiting" ? existing?.activity?.capacityWait : void 0;
|
|
1001
|
+
const activity = terminal || paused || responseCompleted || existing?.responseCompletion ? void 0 : {
|
|
1002
|
+
status: suspension || (cancelling ? "cancelling" : "running"),
|
|
1003
|
+
...suspension === "waiting" && capacityWait ? { capacityWait } : {}
|
|
1004
|
+
};
|
|
915
1005
|
const eventThreadId = typeof payload.thread_id === "string" && payload.thread_id.trim() ? payload.thread_id.trim() : null;
|
|
916
1006
|
const contextApplies = (!state.activeThreadId || !eventThreadId || eventThreadId === state.activeThreadId) && (!state.activeRunId || !runId || runId === state.activeRunId);
|
|
917
1007
|
const contextPayload = payload.context_window && typeof payload.context_window === "object" ? { ...payload.context_window, run_id: runId } : payload.context_window;
|
|
@@ -946,6 +1036,7 @@ function applyRuntimeEvent(state, event) {
|
|
|
946
1036
|
content,
|
|
947
1037
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
948
1038
|
isFinal: terminal || responseCompleted,
|
|
1039
|
+
activity,
|
|
949
1040
|
responseCompletion: completion,
|
|
950
1041
|
parts
|
|
951
1042
|
}),
|
|
@@ -955,6 +1046,7 @@ function applyRuntimeEvent(state, event) {
|
|
|
955
1046
|
content,
|
|
956
1047
|
parts,
|
|
957
1048
|
isFinal: terminal || responseCompleted || message.isFinal,
|
|
1049
|
+
activity,
|
|
958
1050
|
responseCompletion: completion || message.responseCompletion
|
|
959
1051
|
})
|
|
960
1052
|
});
|
|
@@ -964,10 +1056,12 @@ function applyRuntimeEvent(state, event) {
|
|
|
964
1056
|
return {
|
|
965
1057
|
...message,
|
|
966
1058
|
isFinal: true,
|
|
1059
|
+
activity: void 0,
|
|
967
1060
|
responseCompletion: completion,
|
|
968
1061
|
...segment ? { durableId: segment.message_id } : {}
|
|
969
1062
|
};
|
|
970
1063
|
});
|
|
1064
|
+
if (terminal || distinctSegment) messages = messages.map((message) => message.role === "assistant" && message.runId === runId && message.activity && (terminal || message.id !== (existing?.id || messageId)) ? { ...message, activity: void 0 } : message);
|
|
971
1065
|
const activityStatus = suspension || (paused ? "paused" : cancelling ? "cancelling" : "running");
|
|
972
1066
|
const terminalThread = runId && !sealedAfterTerminal ? {
|
|
973
1067
|
active_run: terminal ? null : {
|
|
@@ -2130,6 +2224,12 @@ function useAgentChat(options = {}) {
|
|
|
2130
2224
|
}
|
|
2131
2225
|
}
|
|
2132
2226
|
}, [clearOperationError, client, detach, failOperation, pageSize, queue.hydrate, runtime, update, invalidateThreadOpen, ownOperation, captureFeedbackRead]);
|
|
2227
|
+
const consumePreview = React8.useCallback((frame) => {
|
|
2228
|
+
update((current) => {
|
|
2229
|
+
if (frame && current.activeRunId && current.activeRunId !== frame.run_id) return current;
|
|
2230
|
+
return { ...current, assistantPreview: acceptAssistantPreview(current.assistantPreview, frame) };
|
|
2231
|
+
});
|
|
2232
|
+
}, [update]);
|
|
2133
2233
|
const consumeEvent = React8.useCallback((event) => {
|
|
2134
2234
|
const timing = continuationTiming.current.get(event.run_id);
|
|
2135
2235
|
if (timing && !timing.attached) {
|
|
@@ -2158,7 +2258,9 @@ function useAgentChat(options = {}) {
|
|
|
2158
2258
|
clearOperationError("attach_run");
|
|
2159
2259
|
update((current) => ({ ...current, activeRunId: runId, runState: "reconnecting", error: null }));
|
|
2160
2260
|
try {
|
|
2161
|
-
const result = await client.runs.attach({ runId, cursor,
|
|
2261
|
+
const result = await client.runs.attach({ runId, cursor, onPreview: (frame) => {
|
|
2262
|
+
if (isCurrent()) consumePreview(frame);
|
|
2263
|
+
}, leaseSeconds: 60, signal: controller.signal }, (event) => {
|
|
2162
2264
|
if (isCurrent()) consumeEvent(event);
|
|
2163
2265
|
});
|
|
2164
2266
|
if (!isCurrent()) return;
|
|
@@ -2174,7 +2276,7 @@ function useAgentChat(options = {}) {
|
|
|
2174
2276
|
} finally {
|
|
2175
2277
|
if (streamControllerRef.current === controller) streamControllerRef.current = null;
|
|
2176
2278
|
}
|
|
2177
|
-
}, [clearOperationError, client, consumeEvent, eventBuffer, failOperation, refreshMessages, runtime, update, ownOperation]);
|
|
2279
|
+
}, [clearOperationError, client, consumeEvent, consumePreview, eventBuffer, failOperation, refreshMessages, runtime, update, ownOperation]);
|
|
2178
2280
|
attachRunRef.current = attachRun;
|
|
2179
2281
|
const submit = React8.useCallback(async (input) => {
|
|
2180
2282
|
if (!input.text.trim() && !input.attachmentIds?.length && !input.uploads?.length) return;
|
|
@@ -2285,6 +2387,9 @@ function useAgentChat(options = {}) {
|
|
|
2285
2387
|
}));
|
|
2286
2388
|
}
|
|
2287
2389
|
const result = await client.chat.stream({
|
|
2390
|
+
onPreview: (frame) => {
|
|
2391
|
+
if (isCurrent()) consumePreview(frame);
|
|
2392
|
+
},
|
|
2288
2393
|
leaseSeconds: 60,
|
|
2289
2394
|
clientSubmissionId: ticket.client_submission_id,
|
|
2290
2395
|
input: input.text.trim(),
|
|
@@ -2336,7 +2441,7 @@ function useAgentChat(options = {}) {
|
|
|
2336
2441
|
} catch (error) {
|
|
2337
2442
|
if (submissionOwner() || !isAbortLike(error)) throw error;
|
|
2338
2443
|
}
|
|
2339
|
-
}, [clearOperationError, client, consumeEvent, detach, eventBuffer, failOperation, invalidateThreadOpen, queue.submit, refreshMessages, refreshThreads, runtime, update, ownOperation]);
|
|
2444
|
+
}, [clearOperationError, client, consumeEvent, consumePreview, detach, eventBuffer, failOperation, invalidateThreadOpen, queue.submit, refreshMessages, refreshThreads, runtime, update, ownOperation]);
|
|
2340
2445
|
const { steer, editPendingMessage, steerPendingMessage, removePendingMessage, resumeQueue } = useAgentChatQueueActions({
|
|
2341
2446
|
queue,
|
|
2342
2447
|
stateRef,
|
|
@@ -2563,6 +2668,7 @@ function useAgentChat(options = {}) {
|
|
|
2563
2668
|
const hasMoreThreads = threadTotal === void 0 ? state.threads.length >= threadPageSize : state.threads.length < threadTotal;
|
|
2564
2669
|
return {
|
|
2565
2670
|
...state,
|
|
2671
|
+
messages: messagesWithAssistantPreview(state.messages, state.assistantPreview),
|
|
2566
2672
|
activeThread,
|
|
2567
2673
|
pendingMessages,
|
|
2568
2674
|
messageQueue,
|