@springbrand/chat-client 0.1.3-alpha.41 → 0.1.3-alpha.42
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@springbrand/chat-client",
|
|
3
|
-
"version": "0.1.3-alpha.
|
|
3
|
+
"version": "0.1.3-alpha.42",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"src",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"@types/react-dom": "^19.2.5",
|
|
28
28
|
"react-dom": "^19.2.8",
|
|
29
29
|
"typescript": "^7.0.2",
|
|
30
|
-
"@springbrand/agent-runtime": "0.2.0-alpha.
|
|
30
|
+
"@springbrand/agent-runtime": "0.2.0-alpha.54"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
33
|
"typecheck": "tsc --noEmit"
|
|
@@ -34,6 +34,12 @@ type ChatMessageMetadata = Record<string, unknown> & {
|
|
|
34
34
|
completedAt?: number;
|
|
35
35
|
error?: string;
|
|
36
36
|
interruptedByUser?: boolean;
|
|
37
|
+
// AI SDK keeps one assistant UIMessage for the live stream. This local-only
|
|
38
|
+
// anchor lets presentation place later steps below the steer that caused them.
|
|
39
|
+
optimisticSteer?: {
|
|
40
|
+
assistantMessageId: string;
|
|
41
|
+
afterStep: number;
|
|
42
|
+
};
|
|
37
43
|
requestedCapabilities?: readonly RequestedCapability[];
|
|
38
44
|
turnId?: string;
|
|
39
45
|
turnStatus?: string;
|
|
@@ -369,7 +375,35 @@ export function mergeOptimisticMessages<T extends UIMessage>(
|
|
|
369
375
|
if (optimistic.length === 0) return messages;
|
|
370
376
|
const ids = new Set(messages.map(({ id }) => id));
|
|
371
377
|
const pending = optimistic.filter(({ id }) => !ids.has(id));
|
|
372
|
-
|
|
378
|
+
if (pending.length === 0) return messages;
|
|
379
|
+
const steer = pending.find((message) =>
|
|
380
|
+
(message.metadata as ChatMessageMetadata | undefined)?.optimisticSteer
|
|
381
|
+
);
|
|
382
|
+
const anchor = (steer?.metadata as ChatMessageMetadata | undefined)
|
|
383
|
+
?.optimisticSteer;
|
|
384
|
+
const assistantIndex = anchor
|
|
385
|
+
? messages.findIndex(({ id, role }) =>
|
|
386
|
+
id === anchor.assistantMessageId && role === "assistant"
|
|
387
|
+
)
|
|
388
|
+
: -1;
|
|
389
|
+
if (!steer || !anchor || assistantIndex < 0) return [...messages, ...pending];
|
|
390
|
+
|
|
391
|
+
const assistant = messages[assistantIndex]!;
|
|
392
|
+
let step = 0;
|
|
393
|
+
const splitAt = assistant.parts.findIndex((part) => {
|
|
394
|
+
if (part.type !== "step-start") return false;
|
|
395
|
+
step += 1;
|
|
396
|
+
return step > anchor.afterStep;
|
|
397
|
+
});
|
|
398
|
+
if (splitAt < 0) return [...messages, ...pending];
|
|
399
|
+
|
|
400
|
+
return [
|
|
401
|
+
...messages.slice(0, assistantIndex),
|
|
402
|
+
{ ...assistant, id: `${assistant.id}:before:${steer.id}`, parts: assistant.parts.slice(0, splitAt) },
|
|
403
|
+
...pending,
|
|
404
|
+
{ ...assistant, id: `${assistant.id}:after:${steer.id}`, parts: assistant.parts.slice(splitAt) },
|
|
405
|
+
...messages.slice(assistantIndex + 1),
|
|
406
|
+
];
|
|
373
407
|
}
|
|
374
408
|
|
|
375
409
|
function queuedPreview(message: ChatMessage): string {
|
|
@@ -753,6 +787,32 @@ export function useUniversalAgentChat(): ChatRuntime {
|
|
|
753
787
|
rollback();
|
|
754
788
|
return true;
|
|
755
789
|
}
|
|
790
|
+
if (delivery === "steer" && receipt.kind === "accepted") {
|
|
791
|
+
const activeAssistant = messagesRef.current.findLast((candidate) =>
|
|
792
|
+
candidate.role === "assistant" &&
|
|
793
|
+
candidate.metadata?.turnId === submissionId
|
|
794
|
+
);
|
|
795
|
+
const assistantMessageId = activeAssistant?.id ??
|
|
796
|
+
(authoritativeTurn?.activeSubmissionId === submissionId
|
|
797
|
+
? authoritativeTurn.activeMessageId
|
|
798
|
+
: undefined);
|
|
799
|
+
if (assistantMessageId) {
|
|
800
|
+
const afterStep = activeAssistant?.parts.filter(
|
|
801
|
+
({ type }) => type === "step-start",
|
|
802
|
+
).length ?? 0;
|
|
803
|
+
setOptimisticMessages((current) => current.map((optimistic) =>
|
|
804
|
+
optimistic.id === message.id
|
|
805
|
+
? {
|
|
806
|
+
...optimistic,
|
|
807
|
+
metadata: {
|
|
808
|
+
...optimistic.metadata,
|
|
809
|
+
optimisticSteer: { assistantMessageId, afterStep },
|
|
810
|
+
},
|
|
811
|
+
}
|
|
812
|
+
: optimistic
|
|
813
|
+
));
|
|
814
|
+
}
|
|
815
|
+
}
|
|
756
816
|
if (turnTimingRef.current?.messageId === message.id) {
|
|
757
817
|
turnTimingRef.current.submissionId = submissionId;
|
|
758
818
|
}
|
|
@@ -785,7 +845,13 @@ export function useUniversalAgentChat(): ChatRuntime {
|
|
|
785
845
|
);
|
|
786
846
|
return false;
|
|
787
847
|
}
|
|
788
|
-
}, [
|
|
848
|
+
}, [
|
|
849
|
+
authoritativeTurn?.activeMessageId,
|
|
850
|
+
authoritativeTurn?.activeSubmissionId,
|
|
851
|
+
chatId,
|
|
852
|
+
clearError,
|
|
853
|
+
dispatchMessage,
|
|
854
|
+
]);
|
|
789
855
|
|
|
790
856
|
const sendText = useCallback(async (
|
|
791
857
|
text: string,
|