@agent-native/toolkit 0.226.0 → 0.226.1-nightly-20261107004051
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/dist/app/agentkit/react/components.d.ts +11 -9
- package/dist/app/agentkit/react/components.d.ts.map +1 -1
- package/dist/app/agentkit/react/components.js +115 -111
- package/dist/app/agentkit/react/components.js.map +1 -1
- package/dist/app/agentkit/react/stop-button.js +5 -5
- package/dist/app/agentkit/react/stop-button.js.map +1 -1
- package/dist/app/agentkit/react/styles.css +19 -48
- package/dist/app/auth/AuthPage.d.ts.map +1 -1
- package/dist/app/auth/AuthPage.js +7 -5
- package/dist/app/auth/AuthPage.js.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.js +7 -2
- package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
- package/dist/app/integrations/IntegrationDetailPage.d.ts.map +1 -1
- package/dist/app/integrations/IntegrationDetailPage.js +43 -1
- package/dist/app/integrations/IntegrationDetailPage.js.map +1 -1
- package/dist/app/integrations/IntegrationsPage.d.ts.map +1 -1
- package/dist/app/integrations/IntegrationsPage.js +16 -7
- package/dist/app/integrations/IntegrationsPage.js.map +1 -1
- package/dist/app/integrations/connected-server-id.d.ts +17 -0
- package/dist/app/integrations/connected-server-id.d.ts.map +1 -0
- package/dist/app/integrations/connected-server-id.js +26 -0
- package/dist/app/integrations/connected-server-id.js.map +1 -0
- package/dist/composer/TiptapComposer.d.ts +5 -3
- package/dist/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/composer/TiptapComposer.js +1 -1
- package/dist/composer/TiptapComposer.js.map +1 -1
- package/package.json +4 -4
- package/src/app/agentkit/react/components.interaction.spec.tsx +112 -0
- package/src/app/agentkit/react/components.tsx +178 -192
- package/src/app/agentkit/react/lifecycle.spec.tsx +5 -16
- package/src/app/agentkit/react/stop-button.spec.tsx +1 -1
- package/src/app/agentkit/react/stop-button.tsx +5 -5
- package/src/app/agentkit/react/styles.css +19 -48
- package/src/app/agentkit/react/work-handoff.spec.tsx +202 -69
- package/src/app/auth/AuthPage.interaction.spec.tsx +17 -1
- package/src/app/auth/AuthPage.tsx +12 -4
- package/src/app/chat/AgentKitAssistantChat.tsx +6 -2
- package/src/app/integrations/IntegrationDetailPage.spec.tsx +30 -0
- package/src/app/integrations/IntegrationDetailPage.tsx +85 -1
- package/src/app/integrations/IntegrationsPage.spec.tsx +74 -0
- package/src/app/integrations/IntegrationsPage.tsx +47 -20
- package/src/app/integrations/connected-server-id.ts +34 -0
- package/src/composer/TiptapComposer.spec.ts +7 -7
- package/src/composer/TiptapComposer.tsx +12 -13
- package/src/composer/TiptapComposer.voice-toolbar.spec.tsx +24 -1
|
@@ -2268,18 +2268,21 @@ export const AgentActivityGroup = memo(function AgentActivityGroup({
|
|
|
2268
2268
|
messageId,
|
|
2269
2269
|
foldedReasoningMessageId,
|
|
2270
2270
|
summarizesTurn = false,
|
|
2271
|
+
part,
|
|
2271
2272
|
}: {
|
|
2272
|
-
runId?: RunId;
|
|
2273
|
-
afterSequence?: number;
|
|
2274
|
-
throughSequence?: number;
|
|
2275
|
-
excludeAgentActivities?: boolean;
|
|
2276
|
-
isCurrentSegment?: boolean;
|
|
2273
|
+
runId?: RunId | undefined;
|
|
2274
|
+
afterSequence?: number | undefined;
|
|
2275
|
+
throughSequence?: number | undefined;
|
|
2276
|
+
excludeAgentActivities?: boolean | undefined;
|
|
2277
|
+
isCurrentSegment?: boolean | undefined;
|
|
2277
2278
|
/** The reply this work segment precedes. */
|
|
2278
|
-
messageId?: string;
|
|
2279
|
+
messageId?: string | undefined;
|
|
2279
2280
|
/** A saved reply whose reasoning before its answer joins this group. */
|
|
2280
|
-
foldedReasoningMessageId?: string;
|
|
2281
|
+
foldedReasoningMessageId?: string | undefined;
|
|
2281
2282
|
/** This segment is its run's whole turn, so it shows a row with no work. */
|
|
2282
|
-
summarizesTurn?: boolean;
|
|
2283
|
+
summarizesTurn?: boolean | undefined;
|
|
2284
|
+
/** Only the steps' result cards, or only the work row; both by default. */
|
|
2285
|
+
part?: "results" | "row" | undefined;
|
|
2283
2286
|
}) {
|
|
2284
2287
|
const {
|
|
2285
2288
|
threadId,
|
|
@@ -2598,6 +2601,15 @@ export const AgentActivityGroup = memo(function AgentActivityGroup({
|
|
|
2598
2601
|
minute: labels.durationMinuteShort,
|
|
2599
2602
|
second: labels.durationSecondShort,
|
|
2600
2603
|
});
|
|
2604
|
+
const resultCards =
|
|
2605
|
+
durableToolResults.length && part !== "row" ? (
|
|
2606
|
+
<div data-agentkit-tool-results="true">
|
|
2607
|
+
{durableToolResults.map(({ tool, Renderer }) => (
|
|
2608
|
+
<Renderer key={tool.id} value={tool} threadId={threadId} />
|
|
2609
|
+
))}
|
|
2610
|
+
</div>
|
|
2611
|
+
) : null;
|
|
2612
|
+
if (part === "results") return resultCards;
|
|
2601
2613
|
if (
|
|
2602
2614
|
activityItems.length === 0 &&
|
|
2603
2615
|
durableToolResults.length === 0 &&
|
|
@@ -2732,13 +2744,7 @@ export const AgentActivityGroup = memo(function AgentActivityGroup({
|
|
|
2732
2744
|
};
|
|
2733
2745
|
return (
|
|
2734
2746
|
<>
|
|
2735
|
-
{
|
|
2736
|
-
<div data-agentkit-tool-results="true">
|
|
2737
|
-
{durableToolResults.map(({ tool, Renderer }) => (
|
|
2738
|
-
<Renderer key={tool.id} value={tool} threadId={threadId} />
|
|
2739
|
-
))}
|
|
2740
|
-
</div>
|
|
2741
|
-
) : null}
|
|
2747
|
+
{resultCards}
|
|
2742
2748
|
{hasExpandableActivity || activelyWorking ? (
|
|
2743
2749
|
<AgentWorkDisclosure
|
|
2744
2750
|
label={baseSummaryLabel}
|
|
@@ -3353,7 +3359,13 @@ function AgentWorkDisclosure({
|
|
|
3353
3359
|
children: ReactNode;
|
|
3354
3360
|
}) {
|
|
3355
3361
|
const { slots, labels, workedForRowLayout } = useAgentKit();
|
|
3356
|
-
const
|
|
3362
|
+
const row = useContext(AgentWorkRowOpenContext);
|
|
3363
|
+
const [open, setOpen] = useState(() => row?.opened.has(row.key) ?? false);
|
|
3364
|
+
useEffect(() => {
|
|
3365
|
+
if (!row) return;
|
|
3366
|
+
if (open) row.opened.add(row.key);
|
|
3367
|
+
else row.opened.delete(row.key);
|
|
3368
|
+
}, [open, row]);
|
|
3357
3369
|
const contentId = useId();
|
|
3358
3370
|
const Summary = slots.workedForSummary;
|
|
3359
3371
|
const failed =
|
|
@@ -3368,9 +3380,7 @@ function AgentWorkDisclosure({
|
|
|
3368
3380
|
? failed.split("{{summary}}")
|
|
3369
3381
|
: ["", ""];
|
|
3370
3382
|
const fullLabel = failed ? failed.replace("{{summary}}", () => label) : label;
|
|
3371
|
-
const
|
|
3372
|
-
const footerHost = footer?.element ?? null;
|
|
3373
|
-
const handoff = useAgentWorkHandoff(running, footer, setOpen);
|
|
3383
|
+
const footerHost = useAgentMessageWorkHost(messageId);
|
|
3374
3384
|
const summaryLabel = label ? (
|
|
3375
3385
|
<span className="agentkit-activities-label">
|
|
3376
3386
|
{failedBefore || failedAfter ? (
|
|
@@ -3403,12 +3413,10 @@ function AgentWorkDisclosure({
|
|
|
3403
3413
|
// work with nothing to expand is text, not a disabled button.
|
|
3404
3414
|
const disclosure = (
|
|
3405
3415
|
<div
|
|
3406
|
-
ref={handoff.inPlaceRef}
|
|
3407
3416
|
className="agentkit-activities"
|
|
3408
3417
|
data-running={running ? "true" : undefined}
|
|
3409
3418
|
data-expandable={expandable ? "true" : "false"}
|
|
3410
3419
|
data-open={open ? "true" : "false"}
|
|
3411
|
-
data-handoff={handoff.leavingHeight ? "enter" : undefined}
|
|
3412
3420
|
>
|
|
3413
3421
|
<AgentKitDisclosureSummary
|
|
3414
3422
|
as={expandable || running ? "button" : "static"}
|
|
@@ -3437,92 +3445,9 @@ function AgentWorkDisclosure({
|
|
|
3437
3445
|
</AgentKitDisclosureContent>
|
|
3438
3446
|
</div>
|
|
3439
3447
|
);
|
|
3440
|
-
//
|
|
3441
|
-
//
|
|
3442
|
-
|
|
3443
|
-
// reply glides up instead of jumping.
|
|
3444
|
-
return (
|
|
3445
|
-
<>
|
|
3446
|
-
{handoff.leavingHeight ? (
|
|
3447
|
-
<div
|
|
3448
|
-
className="agentkit-work-handoff"
|
|
3449
|
-
data-agentkit-work-handoff="leave"
|
|
3450
|
-
aria-hidden="true"
|
|
3451
|
-
inert
|
|
3452
|
-
style={
|
|
3453
|
-
{
|
|
3454
|
-
"--agentkit-work-handoff-size": `${handoff.leavingHeight}px`,
|
|
3455
|
-
} as CSSProperties
|
|
3456
|
-
}
|
|
3457
|
-
>
|
|
3458
|
-
<AgentKitDisclosureSummary
|
|
3459
|
-
as="static"
|
|
3460
|
-
className="agentkit-activities-summary"
|
|
3461
|
-
rowClassName="agentkit-activities-row"
|
|
3462
|
-
open={false}
|
|
3463
|
-
onToggle={noop}
|
|
3464
|
-
expandable={false}
|
|
3465
|
-
style={workedForRowSummaryStyle(workedForRowLayout)}
|
|
3466
|
-
icon={icon}
|
|
3467
|
-
label={summaryLabel}
|
|
3468
|
-
>
|
|
3469
|
-
{summaryContent}
|
|
3470
|
-
</AgentKitDisclosureSummary>
|
|
3471
|
-
</div>
|
|
3472
|
-
) : null}
|
|
3473
|
-
{footerHost ? createPortal(disclosure, footerHost) : disclosure}
|
|
3474
|
-
</>
|
|
3475
|
-
);
|
|
3476
|
-
}
|
|
3477
|
-
|
|
3478
|
-
const AGENT_WORK_HANDOFF_MS = 180;
|
|
3479
|
-
|
|
3480
|
-
/**
|
|
3481
|
-
* Tracks a work row's move from above its reply to the reply's footer. Only
|
|
3482
|
-
* a finish seen live animates (the row ran, or its reply streamed): a saved
|
|
3483
|
-
* thread renders the row in place for one unpainted pass before its host
|
|
3484
|
-
* registers, and that must not read as a finish. A row opened to watch live
|
|
3485
|
-
* work closes as it moves, so its steps never land below the reply.
|
|
3486
|
-
*/
|
|
3487
|
-
function useAgentWorkHandoff(
|
|
3488
|
-
running: boolean | undefined,
|
|
3489
|
-
footer: AgentMessageWorkHost | null,
|
|
3490
|
-
setOpen: (open: boolean) => void,
|
|
3491
|
-
) {
|
|
3492
|
-
const prefersReducedMotion = usePrefersReducedMotion();
|
|
3493
|
-
const footerHost = footer?.element ?? null;
|
|
3494
|
-
const live = footer?.live ?? false;
|
|
3495
|
-
const inPlaceRef = useRef<HTMLDivElement>(null);
|
|
3496
|
-
const sawRunningRef = useRef(false);
|
|
3497
|
-
const inPlaceHeightRef = useRef(0);
|
|
3498
|
-
const previousHostRef = useRef<HTMLElement | null>(null);
|
|
3499
|
-
const [leavingHeight, setLeavingHeight] = useState(0);
|
|
3500
|
-
// Measured on every in-place commit, because once the row portals away
|
|
3501
|
-
// its old box is gone. The placeholder keeps the row's margins from the
|
|
3502
|
-
// stylesheet, so only the border box is carried over.
|
|
3503
|
-
useLayoutEffect(() => {
|
|
3504
|
-
if (running) sawRunningRef.current = true;
|
|
3505
|
-
if (footerHost || !inPlaceRef.current) return;
|
|
3506
|
-
inPlaceHeightRef.current = inPlaceRef.current.offsetHeight;
|
|
3507
|
-
});
|
|
3508
|
-
useLayoutEffect(() => {
|
|
3509
|
-
const previousHost = previousHostRef.current;
|
|
3510
|
-
previousHostRef.current = footerHost;
|
|
3511
|
-
if (!footerHost || previousHost) return;
|
|
3512
|
-
if (!live && !sawRunningRef.current) return;
|
|
3513
|
-
setOpen(false);
|
|
3514
|
-
if (prefersReducedMotion || inPlaceHeightRef.current <= 0) return;
|
|
3515
|
-
setLeavingHeight(inPlaceHeightRef.current);
|
|
3516
|
-
}, [footerHost, live, prefersReducedMotion, setOpen]);
|
|
3517
|
-
useEffect(() => {
|
|
3518
|
-
if (!leavingHeight) return;
|
|
3519
|
-
const timer = setTimeout(
|
|
3520
|
-
() => setLeavingHeight(0),
|
|
3521
|
-
AGENT_WORK_HANDOFF_MS + 60,
|
|
3522
|
-
);
|
|
3523
|
-
return () => clearTimeout(timer);
|
|
3524
|
-
}, [leavingHeight]);
|
|
3525
|
-
return { inPlaceRef, leavingHeight: footerHost ? leavingHeight : 0 };
|
|
3448
|
+
// A reply's work sits on its footer line, live or finished, so the finish
|
|
3449
|
+
// never moves it.
|
|
3450
|
+
return footerHost ? createPortal(disclosure, footerHost) : disclosure;
|
|
3526
3451
|
}
|
|
3527
3452
|
|
|
3528
3453
|
function AgentReasoningPartDisclosure({
|
|
@@ -4903,38 +4828,29 @@ function selectedRunModel(
|
|
|
4903
4828
|
/**
|
|
4904
4829
|
* A finished reply's footer offers a host under its message id, and the
|
|
4905
4830
|
* reply's work row portals into it so it shares the line with the controls.
|
|
4906
|
-
* `live` says the footer saw its reply stream, so the row arriving is a
|
|
4907
|
-
* finish the reader watched, not a saved reply loading.
|
|
4908
4831
|
*/
|
|
4909
|
-
interface AgentMessageWorkHost {
|
|
4910
|
-
element: HTMLElement;
|
|
4911
|
-
live: boolean;
|
|
4912
|
-
}
|
|
4913
|
-
|
|
4914
4832
|
interface AgentMessageWorkHosts {
|
|
4915
|
-
get: (messageId: string) =>
|
|
4916
|
-
set: (messageId: string,
|
|
4833
|
+
get: (messageId: string) => HTMLElement | undefined;
|
|
4834
|
+
set: (messageId: string, element: HTMLElement) => void;
|
|
4917
4835
|
release: (messageId: string, element: HTMLElement) => void;
|
|
4918
4836
|
subscribe: (listener: () => void) => () => void;
|
|
4919
4837
|
}
|
|
4920
4838
|
|
|
4921
4839
|
function createAgentMessageWorkHosts(): AgentMessageWorkHosts {
|
|
4922
|
-
const hosts = new Map<string,
|
|
4840
|
+
const hosts = new Map<string, HTMLElement>();
|
|
4923
4841
|
const listeners = new Set<() => void>();
|
|
4924
4842
|
const notify = () => {
|
|
4925
4843
|
for (const listener of listeners) listener();
|
|
4926
4844
|
};
|
|
4927
4845
|
return {
|
|
4928
4846
|
get: (messageId) => hosts.get(messageId),
|
|
4929
|
-
set(messageId,
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
return;
|
|
4933
|
-
hosts.set(messageId, host);
|
|
4847
|
+
set(messageId, element) {
|
|
4848
|
+
if (hosts.get(messageId) === element) return;
|
|
4849
|
+
hosts.set(messageId, element);
|
|
4934
4850
|
notify();
|
|
4935
4851
|
},
|
|
4936
4852
|
release(messageId, element) {
|
|
4937
|
-
if (hosts.get(messageId)
|
|
4853
|
+
if (hosts.get(messageId) !== element) return;
|
|
4938
4854
|
hosts.delete(messageId);
|
|
4939
4855
|
notify();
|
|
4940
4856
|
},
|
|
@@ -4945,6 +4861,16 @@ function createAgentMessageWorkHosts(): AgentMessageWorkHosts {
|
|
|
4945
4861
|
};
|
|
4946
4862
|
}
|
|
4947
4863
|
|
|
4864
|
+
/**
|
|
4865
|
+
* The work rows the reader opened, by run and segment. A segment's row
|
|
4866
|
+
* remounts when narration hides it and again when the run finishes, and an
|
|
4867
|
+
* opened row comes back open.
|
|
4868
|
+
*/
|
|
4869
|
+
const AgentWorkRowOpenContext = createContext<{
|
|
4870
|
+
key: string;
|
|
4871
|
+
opened: Set<string>;
|
|
4872
|
+
} | null>(null);
|
|
4873
|
+
|
|
4948
4874
|
const AgentMessageWorkHostsContext =
|
|
4949
4875
|
createContext<AgentMessageWorkHosts | null>(null);
|
|
4950
4876
|
|
|
@@ -4954,7 +4880,7 @@ function noopSubscribe() {
|
|
|
4954
4880
|
|
|
4955
4881
|
function useAgentMessageWorkHost(
|
|
4956
4882
|
messageId: string | undefined,
|
|
4957
|
-
):
|
|
4883
|
+
): HTMLElement | null {
|
|
4958
4884
|
const hosts = useContext(AgentMessageWorkHostsContext);
|
|
4959
4885
|
return useSyncExternalStore(
|
|
4960
4886
|
hosts?.subscribe ?? noopSubscribe,
|
|
@@ -4963,23 +4889,17 @@ function useAgentMessageWorkHost(
|
|
|
4963
4889
|
);
|
|
4964
4890
|
}
|
|
4965
4891
|
|
|
4966
|
-
/**
|
|
4967
|
-
* A callback ref that offers its element as a reply's work row host once
|
|
4968
|
-
* the reply stops streaming.
|
|
4969
|
-
*/
|
|
4892
|
+
/** A callback ref that offers its element as a reply's work row host. */
|
|
4970
4893
|
function useAgentMessageWorkHostRef(
|
|
4971
4894
|
messageId: string | undefined,
|
|
4972
|
-
streaming: boolean,
|
|
4973
4895
|
): (element: HTMLElement | null) => void {
|
|
4974
4896
|
const hosts = useContext(AgentMessageWorkHostsContext);
|
|
4975
4897
|
const [element, setElement] = useState<HTMLElement | null>(null);
|
|
4976
|
-
const sawStreamingRef = useRef(false);
|
|
4977
4898
|
useLayoutEffect(() => {
|
|
4978
|
-
if (
|
|
4979
|
-
|
|
4980
|
-
hosts.set(messageId, { element, live: sawStreamingRef.current });
|
|
4899
|
+
if (!hosts || !messageId || !element) return;
|
|
4900
|
+
hosts.set(messageId, element);
|
|
4981
4901
|
return () => hosts.release(messageId, element);
|
|
4982
|
-
}, [element, hosts, messageId
|
|
4902
|
+
}, [element, hosts, messageId]);
|
|
4983
4903
|
return setElement;
|
|
4984
4904
|
}
|
|
4985
4905
|
|
|
@@ -5376,10 +5296,9 @@ export const AgentMessageActions = memo(function AgentMessageActions({
|
|
|
5376
5296
|
const { messageSeq, requestId, runId } = position;
|
|
5377
5297
|
const isAssistant = message.role === "assistant";
|
|
5378
5298
|
const isLastAssistantMessage = isAssistant && position.isLast;
|
|
5379
|
-
//
|
|
5299
|
+
// The reply's work row sits on this footer line, live or finished.
|
|
5380
5300
|
const workHostRef = useAgentMessageWorkHostRef(
|
|
5381
5301
|
isAssistant ? message.id : undefined,
|
|
5382
|
-
message.status === "streaming",
|
|
5383
5302
|
);
|
|
5384
5303
|
const [copied, setCopied] = useState(false);
|
|
5385
5304
|
const [feedback, setFeedback] = useState<"positive" | "negative" | null>(
|
|
@@ -6166,7 +6085,7 @@ export function AgentRunFailure({
|
|
|
6166
6085
|
</ActionButton>
|
|
6167
6086
|
</div>
|
|
6168
6087
|
) : null}
|
|
6169
|
-
{continueFailed ? (
|
|
6088
|
+
{continueFailed && !superseded ? (
|
|
6170
6089
|
<span className="agentkit-command-error" role="alert">
|
|
6171
6090
|
{labels.continueRunUnavailable}
|
|
6172
6091
|
</span>
|
|
@@ -7801,6 +7720,7 @@ export function AgentKitChat({
|
|
|
7801
7720
|
[editingMessage, messageEditEnabled, setEditingMessage],
|
|
7802
7721
|
);
|
|
7803
7722
|
const [messageWorkHosts] = useState(createAgentMessageWorkHosts);
|
|
7723
|
+
const [openedWorkRows] = useState(() => new Set<string>());
|
|
7804
7724
|
const hasConversation =
|
|
7805
7725
|
thread.messages.length > 0 ||
|
|
7806
7726
|
pendingSubmissions.length > 0 ||
|
|
@@ -8475,6 +8395,12 @@ export function AgentKitChat({
|
|
|
8475
8395
|
return messageId ? [messageId] : [];
|
|
8476
8396
|
}),
|
|
8477
8397
|
);
|
|
8398
|
+
// A saved reply's reasoning before its answer joins the work group above
|
|
8399
|
+
// it, so the turn shows one disclosure; the stream did not place it there.
|
|
8400
|
+
const foldsLeadingReasoning = (message: AgentMessage) =>
|
|
8401
|
+
message.status !== "streaming" &&
|
|
8402
|
+
!groupedReasoningMessageIds.has(message.id) &&
|
|
8403
|
+
leadingReasoningMessage(message) !== undefined;
|
|
8478
8404
|
const hasRunWork = (
|
|
8479
8405
|
runId: RunId,
|
|
8480
8406
|
afterSequence?: number,
|
|
@@ -8487,7 +8413,7 @@ export function AgentKitChat({
|
|
|
8487
8413
|
);
|
|
8488
8414
|
// A message the user sent into a running turn ends the run's work so far,
|
|
8489
8415
|
// even when no reply was written before it.
|
|
8490
|
-
const { runSegmentEnds, steerSequences } = useMemo(() => {
|
|
8416
|
+
const { runSegmentEnds, runSegmentStarts, steerSequences } = useMemo(() => {
|
|
8491
8417
|
const deliveredAt = new Map<string, number>();
|
|
8492
8418
|
for (const event of thread.events) {
|
|
8493
8419
|
if (
|
|
@@ -8499,15 +8425,19 @@ export function AgentKitChat({
|
|
|
8499
8425
|
}
|
|
8500
8426
|
}
|
|
8501
8427
|
const ends = new Map<RunId, { id: string; sequence: number }>();
|
|
8428
|
+
const starts = new Map<RunId, { id: string; sequence: number }>();
|
|
8502
8429
|
const steers = new Map<string, number>();
|
|
8430
|
+
const endSegment = (runId: RunId, id: string, sequence: number) => {
|
|
8431
|
+
const previous = ends.get(runId);
|
|
8432
|
+
if (previous) starts.set(runId, previous);
|
|
8433
|
+
ends.set(runId, { id, sequence });
|
|
8434
|
+
};
|
|
8503
8435
|
for (const message of thread.messages) {
|
|
8504
8436
|
const runId = messageRunIds.get(message.id);
|
|
8505
8437
|
if (!runId) continue;
|
|
8506
8438
|
if (message.role === "assistant") {
|
|
8507
8439
|
const sequence = messageBoundarySequences.get(message.id);
|
|
8508
|
-
if (sequence !== undefined)
|
|
8509
|
-
ends.set(runId, { id: message.id, sequence });
|
|
8510
|
-
}
|
|
8440
|
+
if (sequence !== undefined) endSegment(runId, message.id, sequence);
|
|
8511
8441
|
continue;
|
|
8512
8442
|
}
|
|
8513
8443
|
const sequence =
|
|
@@ -8524,9 +8454,13 @@ export function AgentKitChat({
|
|
|
8524
8454
|
);
|
|
8525
8455
|
if (!workBefore) continue;
|
|
8526
8456
|
steers.set(message.id, sequence);
|
|
8527
|
-
|
|
8457
|
+
endSegment(runId, message.id, sequence);
|
|
8528
8458
|
}
|
|
8529
|
-
return {
|
|
8459
|
+
return {
|
|
8460
|
+
runSegmentEnds: ends,
|
|
8461
|
+
runSegmentStarts: starts,
|
|
8462
|
+
steerSequences: steers,
|
|
8463
|
+
};
|
|
8530
8464
|
}, [
|
|
8531
8465
|
messageBoundarySequences,
|
|
8532
8466
|
messageRunIds,
|
|
@@ -8564,19 +8498,40 @@ export function AgentKitChat({
|
|
|
8564
8498
|
]),
|
|
8565
8499
|
);
|
|
8566
8500
|
const pendingRuns = new Set(pendingRunIds);
|
|
8567
|
-
//
|
|
8568
|
-
//
|
|
8569
|
-
//
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
|
|
8573
|
-
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
8579
|
-
|
|
8501
|
+
// While a run works on its reply, the reply's work is one row below its
|
|
8502
|
+
// latest text, shown while work runs there: above the reply, narration
|
|
8503
|
+
// pushes it out of view, and split at that text, the work before reads as
|
|
8504
|
+
// finished while the run works. Result cards stay above the reply, where
|
|
8505
|
+
// they sit once it finishes. A finished reply with nothing running after
|
|
8506
|
+
// it keeps its row in its footer.
|
|
8507
|
+
const liveReplies = new Map<
|
|
8508
|
+
RunId,
|
|
8509
|
+
{
|
|
8510
|
+
replyId: string;
|
|
8511
|
+
start: { id: string; sequence: number } | undefined;
|
|
8512
|
+
foldsReasoning: boolean;
|
|
8513
|
+
}
|
|
8514
|
+
>();
|
|
8515
|
+
for (const [runId, reply] of lastAssistantMessagesByRun) {
|
|
8516
|
+
if (
|
|
8517
|
+
thread.runs[runId]?.status !== "running" ||
|
|
8518
|
+
runSegmentEnds.get(runId)?.id !== reply.id
|
|
8519
|
+
) {
|
|
8520
|
+
continue;
|
|
8521
|
+
}
|
|
8522
|
+
const start = runSegmentStarts.get(runId);
|
|
8523
|
+
const message = thread.messages.find(({ id }) => id === reply.id);
|
|
8524
|
+
if (
|
|
8525
|
+
(message?.status === "streaming" || pendingRuns.has(runId)) &&
|
|
8526
|
+
hasRunWork(runId, start?.sequence)
|
|
8527
|
+
) {
|
|
8528
|
+
liveReplies.set(runId, {
|
|
8529
|
+
replyId: reply.id,
|
|
8530
|
+
start,
|
|
8531
|
+
foldsReasoning: message ? foldsLeadingReasoning(message) : false,
|
|
8532
|
+
});
|
|
8533
|
+
}
|
|
8534
|
+
}
|
|
8580
8535
|
const runRevision = (runId: RunId) =>
|
|
8581
8536
|
`${runId}:${eventIndex.byRun.get(runId)?.length ?? 0}`;
|
|
8582
8537
|
const renderRunFailure = (runId: RunId) => {
|
|
@@ -8630,6 +8585,7 @@ export function AgentKitChat({
|
|
|
8630
8585
|
messageId,
|
|
8631
8586
|
foldedReasoningMessageId,
|
|
8632
8587
|
summarizesTurn,
|
|
8588
|
+
part,
|
|
8633
8589
|
}: {
|
|
8634
8590
|
runId: RunId;
|
|
8635
8591
|
anchor: string;
|
|
@@ -8638,8 +8594,10 @@ export function AgentKitChat({
|
|
|
8638
8594
|
messageId?: string | undefined;
|
|
8639
8595
|
foldedReasoningMessageId?: string | undefined;
|
|
8640
8596
|
summarizesTurn?: boolean | undefined;
|
|
8597
|
+
part?: "results" | "row" | undefined;
|
|
8641
8598
|
}) => {
|
|
8642
|
-
const
|
|
8599
|
+
const openKey = `${threadId}:${runId}:${anchor}`;
|
|
8600
|
+
const key = `run-work:${openKey}${part === "row" ? ":row" : ""}`;
|
|
8643
8601
|
const resetKey = `${anchor}:${runRevision(runId)}`;
|
|
8644
8602
|
return cachedRowElement(
|
|
8645
8603
|
key,
|
|
@@ -8650,10 +8608,12 @@ export function AgentKitChat({
|
|
|
8650
8608
|
messageId,
|
|
8651
8609
|
foldedReasoningMessageId,
|
|
8652
8610
|
summarizesTurn,
|
|
8611
|
+
part,
|
|
8653
8612
|
],
|
|
8654
8613
|
() =>
|
|
8655
8614
|
renderRunWorkElement({
|
|
8656
8615
|
key,
|
|
8616
|
+
openKey,
|
|
8657
8617
|
resetKey,
|
|
8658
8618
|
runId,
|
|
8659
8619
|
afterSequence,
|
|
@@ -8661,11 +8621,13 @@ export function AgentKitChat({
|
|
|
8661
8621
|
messageId,
|
|
8662
8622
|
foldedReasoningMessageId,
|
|
8663
8623
|
summarizesTurn,
|
|
8624
|
+
part,
|
|
8664
8625
|
}),
|
|
8665
8626
|
);
|
|
8666
8627
|
};
|
|
8667
8628
|
const renderRunWorkElement = ({
|
|
8668
8629
|
key,
|
|
8630
|
+
openKey,
|
|
8669
8631
|
resetKey,
|
|
8670
8632
|
runId,
|
|
8671
8633
|
afterSequence,
|
|
@@ -8673,8 +8635,10 @@ export function AgentKitChat({
|
|
|
8673
8635
|
messageId,
|
|
8674
8636
|
foldedReasoningMessageId,
|
|
8675
8637
|
summarizesTurn,
|
|
8638
|
+
part,
|
|
8676
8639
|
}: {
|
|
8677
8640
|
key: string;
|
|
8641
|
+
openKey: string;
|
|
8678
8642
|
resetKey: string;
|
|
8679
8643
|
runId: RunId;
|
|
8680
8644
|
afterSequence?: number | undefined;
|
|
@@ -8682,33 +8646,61 @@ export function AgentKitChat({
|
|
|
8682
8646
|
messageId?: string | undefined;
|
|
8683
8647
|
foldedReasoningMessageId?: string | undefined;
|
|
8684
8648
|
summarizesTurn?: boolean | undefined;
|
|
8649
|
+
part?: "results" | "row" | undefined;
|
|
8685
8650
|
}) => (
|
|
8686
8651
|
<AgentKitSurfaceBoundary key={key} surface="activity" resetKey={resetKey}>
|
|
8687
|
-
<
|
|
8688
|
-
|
|
8689
|
-
|
|
8690
|
-
|
|
8691
|
-
|
|
8692
|
-
|
|
8693
|
-
|
|
8694
|
-
|
|
8695
|
-
|
|
8696
|
-
|
|
8697
|
-
|
|
8698
|
-
|
|
8699
|
-
|
|
8700
|
-
|
|
8701
|
-
|
|
8702
|
-
|
|
8703
|
-
|
|
8704
|
-
|
|
8705
|
-
|
|
8706
|
-
|
|
8652
|
+
<AgentWorkRowOpenContext.Provider
|
|
8653
|
+
value={{ key: openKey, opened: openedWorkRows }}
|
|
8654
|
+
>
|
|
8655
|
+
{part === "row" ? null : (
|
|
8656
|
+
<>
|
|
8657
|
+
<AgentCollaborationFeed
|
|
8658
|
+
runId={runId}
|
|
8659
|
+
afterSequence={afterSequence}
|
|
8660
|
+
throughSequence={throughSequence}
|
|
8661
|
+
/>
|
|
8662
|
+
<AgentTaskGroup
|
|
8663
|
+
runId={runId}
|
|
8664
|
+
afterSequence={afterSequence}
|
|
8665
|
+
throughSequence={throughSequence}
|
|
8666
|
+
/>
|
|
8667
|
+
</>
|
|
8668
|
+
)}
|
|
8669
|
+
<AgentActivityGroup
|
|
8670
|
+
runId={runId}
|
|
8671
|
+
afterSequence={afterSequence}
|
|
8672
|
+
throughSequence={throughSequence}
|
|
8673
|
+
excludeAgentActivities
|
|
8674
|
+
isCurrentSegment={throughSequence === undefined}
|
|
8675
|
+
messageId={messageId}
|
|
8676
|
+
foldedReasoningMessageId={foldedReasoningMessageId}
|
|
8677
|
+
summarizesTurn={summarizesTurn}
|
|
8678
|
+
part={part}
|
|
8679
|
+
/>
|
|
8680
|
+
</AgentWorkRowOpenContext.Provider>
|
|
8707
8681
|
</AgentKitSurfaceBoundary>
|
|
8708
8682
|
);
|
|
8709
8683
|
const renderRunTail = (runId: RunId) => {
|
|
8710
8684
|
const items: ReactNode[] = [];
|
|
8711
|
-
|
|
8685
|
+
const liveReply = liveReplies.get(runId);
|
|
8686
|
+
if (liveReply) {
|
|
8687
|
+
// While the reply's text is the newest output the text is the focus,
|
|
8688
|
+
// so the row shows only while work runs after it.
|
|
8689
|
+
if (pendingRuns.has(runId)) {
|
|
8690
|
+
items.push(
|
|
8691
|
+
renderRunWork({
|
|
8692
|
+
runId,
|
|
8693
|
+
anchor: liveReply.start?.id ?? "start",
|
|
8694
|
+
afterSequence: liveReply.start?.sequence,
|
|
8695
|
+
messageId: liveReply.replyId,
|
|
8696
|
+
foldedReasoningMessageId: liveReply.foldsReasoning
|
|
8697
|
+
? liveReply.replyId
|
|
8698
|
+
: undefined,
|
|
8699
|
+
part: "row",
|
|
8700
|
+
}),
|
|
8701
|
+
);
|
|
8702
|
+
}
|
|
8703
|
+
} else if (pendingRuns.has(runId)) {
|
|
8712
8704
|
const boundary = runSegmentEnds.get(runId);
|
|
8713
8705
|
items.push(
|
|
8714
8706
|
renderRunWork({
|
|
@@ -8752,7 +8744,8 @@ export function AgentKitChat({
|
|
|
8752
8744
|
}
|
|
8753
8745
|
const tailRunIds = Array.from(
|
|
8754
8746
|
new Set([
|
|
8755
|
-
...pendingRunIds
|
|
8747
|
+
...pendingRunIds,
|
|
8748
|
+
...liveReplies.keys(),
|
|
8756
8749
|
...Array.from(failedRuns.keys()).filter(
|
|
8757
8750
|
(runId) => !lastAssistantMessagesByRun.has(runId),
|
|
8758
8751
|
),
|
|
@@ -8886,10 +8879,8 @@ export function AgentKitChat({
|
|
|
8886
8879
|
!hasRunWork(runId, sequence) &&
|
|
8887
8880
|
!hasVisibleReasoning(message),
|
|
8888
8881
|
);
|
|
8889
|
-
const
|
|
8890
|
-
isAssistantBoundary &&
|
|
8891
|
-
liveReplyRunIds.has(runId) &&
|
|
8892
|
-
lastAssistantMessagesByRun.get(runId)?.id === message.id;
|
|
8882
|
+
const isLiveReply =
|
|
8883
|
+
isAssistantBoundary && liveReplies.get(runId)?.replyId === message.id;
|
|
8893
8884
|
const rendersRunWork = Boolean(
|
|
8894
8885
|
isAssistantBoundary &&
|
|
8895
8886
|
runId &&
|
|
@@ -8897,26 +8888,21 @@ export function AgentKitChat({
|
|
|
8897
8888
|
hasRunWork(
|
|
8898
8889
|
runId,
|
|
8899
8890
|
previousAssistant?.sequence,
|
|
8900
|
-
|
|
8891
|
+
isLiveReply ? undefined : sequence,
|
|
8901
8892
|
)),
|
|
8902
8893
|
);
|
|
8903
|
-
|
|
8904
|
-
// it, so the turn shows one disclosure; the stream did not place it there.
|
|
8905
|
-
const foldsReasoning =
|
|
8906
|
-
rendersRunWork &&
|
|
8907
|
-
message.status !== "streaming" &&
|
|
8908
|
-
!groupedReasoningMessageIds.has(message.id) &&
|
|
8909
|
-
leadingReasoningMessage(message) !== undefined;
|
|
8894
|
+
const foldsReasoning = rendersRunWork && foldsLeadingReasoning(message);
|
|
8910
8895
|
if (rendersRunWork && runId) {
|
|
8911
8896
|
transcriptItems.push(
|
|
8912
8897
|
renderRunWork({
|
|
8913
8898
|
runId,
|
|
8914
8899
|
anchor: previousAssistant?.id ?? "start",
|
|
8915
8900
|
afterSequence: previousAssistant?.sequence,
|
|
8916
|
-
throughSequence:
|
|
8901
|
+
throughSequence: isLiveReply ? undefined : sequence,
|
|
8917
8902
|
messageId: message.id,
|
|
8918
8903
|
foldedReasoningMessageId: foldsReasoning ? message.id : undefined,
|
|
8919
8904
|
summarizesTurn,
|
|
8905
|
+
part: isLiveReply ? "results" : undefined,
|
|
8920
8906
|
}),
|
|
8921
8907
|
);
|
|
8922
8908
|
}
|
|
@@ -1093,21 +1093,9 @@ describe("AgentChat lifecycle", () => {
|
|
|
1093
1093
|
?.querySelector(".agentkit-message-actions")
|
|
1094
1094
|
?.getAttribute("data-streaming"),
|
|
1095
1095
|
).toBe("true");
|
|
1096
|
-
|
|
1097
|
-
expect(
|
|
1098
|
-
|
|
1099
|
-
).toBe("Worked for 4s");
|
|
1100
|
-
expect(runWorkAfter?.textContent).not.toContain("Thinking");
|
|
1101
|
-
expect(
|
|
1102
|
-
runWorkAfter?.querySelector("[data-agentkit-current-activity]"),
|
|
1103
|
-
).toBeNull();
|
|
1104
|
-
expect(runWorkAfter?.querySelector('[data-status="running"]')).toBeNull();
|
|
1105
|
-
expect(
|
|
1106
|
-
runWorkAfter && response
|
|
1107
|
-
? runWorkAfter.compareDocumentPosition(response) &
|
|
1108
|
-
Node.DOCUMENT_POSITION_FOLLOWING
|
|
1109
|
-
: 0,
|
|
1110
|
-
).not.toBe(0);
|
|
1096
|
+
// oracle-change: while the reply streams after the run's work, the text is the focus, so no work row shows until the run finishes.
|
|
1097
|
+
expect(runWorkAfter).toBeNull();
|
|
1098
|
+
expect(tree.container.textContent).not.toContain("Thinking");
|
|
1111
1099
|
|
|
1112
1100
|
observable.update({
|
|
1113
1101
|
connection: "connected",
|
|
@@ -1566,7 +1554,8 @@ describe("AgentChat lifecycle", () => {
|
|
|
1566
1554
|
expect(
|
|
1567
1555
|
tree.container.querySelector("[data-visible-empty-widget]")?.textContent,
|
|
1568
1556
|
).toBe("Rendered widget");
|
|
1569
|
-
|
|
1557
|
+
// oracle-change: the streaming reply is the focus, so its finished step waits for the footer row instead of a live row.
|
|
1558
|
+
expect(tree.container.querySelector(".agentkit-activities")).toBeNull();
|
|
1570
1559
|
expect(
|
|
1571
1560
|
tree.container.querySelector("[data-agentkit-current-activity]"),
|
|
1572
1561
|
).toBeNull();
|
|
@@ -75,7 +75,7 @@ describe("useAgentKitStopButton", () => {
|
|
|
75
75
|
);
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
it("steps down to secondary only while
|
|
78
|
+
it("steps down to secondary only while End voice chat sits beside it", async () => {
|
|
79
79
|
mocks.activeRunIds = ["run-1"];
|
|
80
80
|
container = document.createElement("div");
|
|
81
81
|
document.body.appendChild(container);
|