@agent-native/toolkit 0.204.0 → 0.204.1-nightly-20261008065100
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/agent-native.eject.json +2 -0
- package/dist/app/agent-page/AgentJobsTab.d.ts.map +1 -1
- package/dist/app/agent-page/AgentJobsTab.js +4 -2
- package/dist/app/agent-page/AgentJobsTab.js.map +1 -1
- package/dist/app/agent-page/AutomationDetailsDialog.d.ts +2 -1
- package/dist/app/agent-page/AutomationDetailsDialog.d.ts.map +1 -1
- package/dist/app/agent-page/AutomationDetailsDialog.js +8 -4
- package/dist/app/agent-page/AutomationDetailsDialog.js.map +1 -1
- package/dist/app/agent-page/ScheduledTriggerNotice.d.ts.map +1 -1
- package/dist/app/agent-page/ScheduledTriggerNotice.js +41 -19
- package/dist/app/agent-page/ScheduledTriggerNotice.js.map +1 -1
- package/dist/app/agent-page/use-jobs.d.ts +1 -1
- package/dist/app/agent-page/use-jobs.d.ts.map +1 -1
- package/dist/app/agentkit/react/components.d.ts.map +1 -1
- package/dist/app/agentkit/react/components.js +124 -45
- package/dist/app/agentkit/react/components.js.map +1 -1
- package/dist/app/agentkit/react/context.d.ts +22 -1
- package/dist/app/agentkit/react/context.d.ts.map +1 -1
- package/dist/app/agentkit/react/context.js +14 -1
- package/dist/app/agentkit/react/context.js.map +1 -1
- package/dist/app/agentkit/react/root.d.ts +3 -2
- package/dist/app/agentkit/react/root.d.ts.map +1 -1
- package/dist/app/agentkit/react/root.js +2 -2
- package/dist/app/agentkit/react/root.js.map +1 -1
- package/dist/app/agentkit/react/styles.css +32 -2
- package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.js +74 -7
- package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
- package/dist/app/chat/AgentPanel.js +1 -1
- package/dist/app/chat/AgentPanel.js.map +1 -1
- package/dist/app/chat/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/MultiTabAssistantChat.js +10 -9
- package/dist/app/chat/MultiTabAssistantChat.js.map +1 -1
- package/dist/app/chat/RunStuckBanner.d.ts +8 -1
- package/dist/app/chat/RunStuckBanner.d.ts.map +1 -1
- package/dist/app/chat/RunStuckBanner.js +68 -6
- package/dist/app/chat/RunStuckBanner.js.map +1 -1
- package/dist/app/chat/chat/run-recovery.d.ts +4 -2
- package/dist/app/chat/chat/run-recovery.d.ts.map +1 -1
- package/dist/app/chat/chat/run-recovery.js +3 -3
- package/dist/app/chat/chat/run-recovery.js.map +1 -1
- package/dist/app/chat/chat/surface-types.d.ts +1 -0
- package/dist/app/chat/chat/surface-types.d.ts.map +1 -1
- package/dist/app/chat/composer/use-mention-search.d.ts +1 -0
- package/dist/app/chat/composer/use-mention-search.d.ts.map +1 -1
- package/dist/app/chat/fallback-chat-title.d.ts +2 -0
- package/dist/app/chat/fallback-chat-title.d.ts.map +1 -0
- package/dist/app/chat/fallback-chat-title.js +11 -0
- package/dist/app/chat/fallback-chat-title.js.map +1 -0
- package/dist/app/chat/feedback-report.d.ts +12 -0
- package/dist/app/chat/feedback-report.d.ts.map +1 -0
- package/dist/app/chat/feedback-report.js +21 -0
- package/dist/app/chat/feedback-report.js.map +1 -0
- package/dist/app/chat/reconcile-settled-run.d.ts +15 -0
- package/dist/app/chat/reconcile-settled-run.d.ts.map +1 -0
- package/dist/app/chat/reconcile-settled-run.js +41 -0
- package/dist/app/chat/reconcile-settled-run.js.map +1 -0
- package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.js +9 -0
- package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.js +9 -0
- package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
- package/dist/app/i18n/catalogs/en-US.d.ts +9 -0
- package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/en-US.js +9 -0
- package/dist/app/i18n/catalogs/en-US.js.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.js +9 -0
- package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.js +9 -0
- package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.js +9 -0
- package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.js +9 -0
- package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.js +9 -0
- package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.js +9 -0
- package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.js +9 -0
- package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.js +9 -0
- package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
- package/dist/app/providers/AppProviders.d.ts.map +1 -1
- package/dist/app/providers/AppProviders.js +7 -1
- package/dist/app/providers/AppProviders.js.map +1 -1
- package/dist/app/settings/shell/pages/audit.d.ts.map +1 -1
- package/dist/app/settings/shell/pages/audit.js +11 -2
- package/dist/app/settings/shell/pages/audit.js.map +1 -1
- package/dist/app/shared/AppShellSkeleton.d.ts.map +1 -1
- package/dist/app/shared/AppShellSkeleton.js +4 -0
- package/dist/app/shared/AppShellSkeleton.js.map +1 -1
- package/dist/composer/ComposerContextMenu.d.ts +14 -1
- package/dist/composer/ComposerContextMenu.d.ts.map +1 -1
- package/dist/composer/ComposerContextMenu.js +65 -19
- package/dist/composer/ComposerContextMenu.js.map +1 -1
- package/dist/composer/MentionPopover.d.ts.map +1 -1
- package/dist/composer/MentionPopover.js +3 -3
- package/dist/composer/MentionPopover.js.map +1 -1
- package/dist/composer/PromptComposer.d.ts +1 -1
- package/dist/composer/PromptComposer.d.ts.map +1 -1
- package/dist/composer/TiptapComposer.d.ts +2 -2
- package/dist/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/composer/TiptapComposer.js +92 -36
- package/dist/composer/TiptapComposer.js.map +1 -1
- package/dist/composer/use-mention-search.d.ts +1 -0
- package/dist/composer/use-mention-search.d.ts.map +1 -1
- package/dist/composer/use-mention-search.js +8 -1
- package/dist/composer/use-mention-search.js.map +1 -1
- package/package.json +4 -4
- package/src/app/agent-page/AgentJobsTab.blocked.spec.tsx +43 -0
- package/src/app/agent-page/AgentJobsTab.tsx +14 -5
- package/src/app/agent-page/AutomationDetailsDialog.tsx +22 -8
- package/src/app/agent-page/ScheduledTriggerNotice.spec.tsx +45 -3
- package/src/app/agent-page/ScheduledTriggerNotice.tsx +52 -31
- package/src/app/agent-page/use-jobs.ts +1 -1
- package/src/app/agentkit/react/components.interaction.spec.tsx +311 -1
- package/src/app/agentkit/react/components.spec.tsx +106 -2
- package/src/app/agentkit/react/components.tsx +217 -80
- package/src/app/agentkit/react/context.tsx +36 -0
- package/src/app/agentkit/react/root.tsx +4 -0
- package/src/app/agentkit/react/styles.css +32 -2
- package/src/app/chat/AgentKitAssistantChat.spec.tsx +140 -18
- package/src/app/chat/AgentKitAssistantChat.tsx +123 -7
- package/src/app/chat/AgentPanel.header.spec.ts +43 -7
- package/src/app/chat/AgentPanel.tsx +1 -1
- package/src/app/chat/MultiTabAssistantChat.spec.tsx +34 -0
- package/src/app/chat/MultiTabAssistantChat.tsx +10 -9
- package/src/app/chat/RunStuckBanner.spec.tsx +258 -17
- package/src/app/chat/RunStuckBanner.tsx +107 -5
- package/src/app/chat/chat/run-recovery.tsx +10 -2
- package/src/app/chat/chat/surface-types.ts +1 -0
- package/src/app/chat/fallback-chat-title.ts +10 -0
- package/src/app/chat/feedback-report.spec.ts +45 -0
- package/src/app/chat/feedback-report.ts +27 -0
- package/src/app/chat/reconcile-settled-run.spec.ts +112 -0
- package/src/app/chat/reconcile-settled-run.ts +57 -0
- package/src/app/i18n/catalogs/ar-SA.ts +12 -0
- package/src/app/i18n/catalogs/de-DE.ts +12 -0
- package/src/app/i18n/catalogs/en-US.ts +12 -0
- package/src/app/i18n/catalogs/es-ES.ts +12 -0
- package/src/app/i18n/catalogs/fr-FR.ts +12 -0
- package/src/app/i18n/catalogs/hi-IN.ts +12 -0
- package/src/app/i18n/catalogs/ja-JP.ts +12 -0
- package/src/app/i18n/catalogs/ko-KR.ts +12 -0
- package/src/app/i18n/catalogs/pt-BR.ts +12 -0
- package/src/app/i18n/catalogs/zh-CN.ts +10 -0
- package/src/app/i18n/catalogs/zh-TW.ts +10 -0
- package/src/app/providers/AppProviders.spec.tsx +29 -0
- package/src/app/providers/AppProviders.tsx +13 -0
- package/src/app/settings/shell/pages/audit.spec.tsx +30 -0
- package/src/app/settings/shell/pages/audit.tsx +12 -1
- package/src/app/shared/AppShellSkeleton.tsx +4 -0
- package/src/composer/ComposerContextMenu.tsx +94 -22
- package/src/composer/ComposerPlusMenu.parity.spec.tsx +218 -233
- package/src/composer/MentionPopover.tsx +12 -3
- package/src/composer/PromptComposer.context.spec.tsx +527 -195
- package/src/composer/PromptComposer.tsx +1 -1
- package/src/composer/TiptapComposer.tsx +105 -37
- package/src/composer/use-mention-search.ts +8 -1
|
@@ -849,6 +849,63 @@ describe("AgentKitAssistantChat host behavior", () => {
|
|
|
849
849
|
);
|
|
850
850
|
});
|
|
851
851
|
|
|
852
|
+
it("uses a sanitized first-message fallback when the thread title is blank", async () => {
|
|
853
|
+
const savedSnapshots = vi.fn();
|
|
854
|
+
const message = {
|
|
855
|
+
id: "message-user-title-fallback",
|
|
856
|
+
role: "user",
|
|
857
|
+
status: "complete",
|
|
858
|
+
createdAt: "2026-10-07T12:00:00.000Z",
|
|
859
|
+
parts: [
|
|
860
|
+
{
|
|
861
|
+
type: "text",
|
|
862
|
+
text: 'Summarize @[the sprint|resource:123]\n<context data-agentkit-context-encoding="entities-v1">Private context</context>',
|
|
863
|
+
},
|
|
864
|
+
],
|
|
865
|
+
};
|
|
866
|
+
chatMocks.thread = {
|
|
867
|
+
...chatMocks.thread,
|
|
868
|
+
thread: {
|
|
869
|
+
id: chatMocks.threadId,
|
|
870
|
+
title: "",
|
|
871
|
+
createdAt: message.createdAt,
|
|
872
|
+
updatedAt: message.createdAt,
|
|
873
|
+
},
|
|
874
|
+
messages: [message],
|
|
875
|
+
events: [],
|
|
876
|
+
activeRunIds: [],
|
|
877
|
+
runs: {},
|
|
878
|
+
approvals: {},
|
|
879
|
+
approvalRunIds: {},
|
|
880
|
+
connectionRequests: {},
|
|
881
|
+
connectionRequestRunIds: {},
|
|
882
|
+
tools: {},
|
|
883
|
+
activities: {},
|
|
884
|
+
queuedMessages: [],
|
|
885
|
+
tasks: {},
|
|
886
|
+
taskGroups: {},
|
|
887
|
+
widgets: {},
|
|
888
|
+
widgetMessageIds: {},
|
|
889
|
+
annotations: {},
|
|
890
|
+
annotationMessageIds: {},
|
|
891
|
+
agents: {},
|
|
892
|
+
agentInteractions: [],
|
|
893
|
+
artifacts: [],
|
|
894
|
+
};
|
|
895
|
+
|
|
896
|
+
const props = baseProps({
|
|
897
|
+
onSaveThread: savedSnapshots,
|
|
898
|
+
runtime: {} as never,
|
|
899
|
+
});
|
|
900
|
+
await mount(props);
|
|
901
|
+
await act(async () => root.render(null));
|
|
902
|
+
|
|
903
|
+
expect(savedSnapshots).toHaveBeenCalledWith(
|
|
904
|
+
chatMocks.threadId,
|
|
905
|
+
expect.objectContaining({ title: "Summarize @the sprint" }),
|
|
906
|
+
);
|
|
907
|
+
});
|
|
908
|
+
|
|
852
909
|
it("shows a retry when chat history fails to load", async () => {
|
|
853
910
|
const retryHistory = vi.fn();
|
|
854
911
|
chatMocks.history = {
|
|
@@ -897,6 +954,38 @@ describe("AgentKitAssistantChat host behavior", () => {
|
|
|
897
954
|
expect(container.querySelector('[role="status"]')?.textContent).toBe(
|
|
898
955
|
"agentChat.status.thinking",
|
|
899
956
|
);
|
|
957
|
+
expect(
|
|
958
|
+
container.querySelector("[data-agentkit-active-run-id-copy]"),
|
|
959
|
+
).toBeNull();
|
|
960
|
+
});
|
|
961
|
+
|
|
962
|
+
it("shows and copies the active run ID as soon as it becomes available", async () => {
|
|
963
|
+
const props = baseProps();
|
|
964
|
+
await mount(props);
|
|
965
|
+
|
|
966
|
+
expect(
|
|
967
|
+
container.querySelector("[data-agentkit-active-run-id-copy]"),
|
|
968
|
+
).toBeNull();
|
|
969
|
+
|
|
970
|
+
chatMocks.thread = { ...chatMocks.thread, activeRunIds: ["run-active"] };
|
|
971
|
+
await act(async () => root.render(<AgentKitAssistantChat {...props} />));
|
|
972
|
+
|
|
973
|
+
const copyRequestIdButton = container.querySelector(
|
|
974
|
+
'[data-agentkit-active-run-id-copy] button[aria-label="agentChat.message.copyRequestId"]',
|
|
975
|
+
);
|
|
976
|
+
expect(copyRequestIdButton).toBeTruthy();
|
|
977
|
+
|
|
978
|
+
await act(async () => {
|
|
979
|
+
copyRequestIdButton?.click();
|
|
980
|
+
await Promise.resolve();
|
|
981
|
+
});
|
|
982
|
+
|
|
983
|
+
expect(chatMocks.writeClipboardText).toHaveBeenCalledWith("run-active");
|
|
984
|
+
expect(
|
|
985
|
+
container.querySelector(
|
|
986
|
+
'[data-agentkit-active-run-id-copy] button[aria-label="agentChat.common.copied"]',
|
|
987
|
+
),
|
|
988
|
+
).toBeTruthy();
|
|
900
989
|
});
|
|
901
990
|
|
|
902
991
|
it("does not duplicate Thinking after the run becomes active", async () => {
|
|
@@ -4015,11 +4104,18 @@ describe("AgentKitAssistantChat host behavior", () => {
|
|
|
4015
4104
|
createdAt: "2026-09-26T12:00:01.000Z",
|
|
4016
4105
|
parts: [{ type: "text", text: "Assistant answer survives handoff" }],
|
|
4017
4106
|
};
|
|
4107
|
+
const followupUserMessage = {
|
|
4108
|
+
id: "handoff-followup-user-message",
|
|
4109
|
+
role: "user",
|
|
4110
|
+
status: "complete",
|
|
4111
|
+
createdAt: "2026-09-26T12:00:02.000Z",
|
|
4112
|
+
parts: [{ type: "text", text: "Save after handoff" }],
|
|
4113
|
+
};
|
|
4018
4114
|
chatMocks.threadId = threadId;
|
|
4019
4115
|
chatMocks.thread = {
|
|
4020
4116
|
thread: {
|
|
4021
4117
|
id: threadId,
|
|
4022
|
-
title: "
|
|
4118
|
+
title: "",
|
|
4023
4119
|
createdAt: message.createdAt,
|
|
4024
4120
|
updatedAt: message.createdAt,
|
|
4025
4121
|
},
|
|
@@ -4056,7 +4152,13 @@ describe("AgentKitAssistantChat host behavior", () => {
|
|
|
4056
4152
|
|
|
4057
4153
|
await act(async () => root.render(null));
|
|
4058
4154
|
expect(savedSnapshots).toHaveBeenCalledOnce();
|
|
4059
|
-
|
|
4155
|
+
expect(savedSnapshots).toHaveBeenCalledWith(
|
|
4156
|
+
threadId,
|
|
4157
|
+
expect.objectContaining({
|
|
4158
|
+
title: "Keep this transcript visible",
|
|
4159
|
+
titleSource: "fallback",
|
|
4160
|
+
}),
|
|
4161
|
+
);
|
|
4060
4162
|
const fetch = vi.fn(async () => new Response(null, { status: 404 }));
|
|
4061
4163
|
const runtime = {
|
|
4062
4164
|
id: "handoff-test",
|
|
@@ -4092,23 +4194,23 @@ describe("AgentKitAssistantChat host behavior", () => {
|
|
|
4092
4194
|
chatMocks.createTransport.mockImplementation((options: any) =>
|
|
4093
4195
|
createAgentNativeAgentKitTransport({ ...options, runtime, fetch }),
|
|
4094
4196
|
);
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
homeIntroSlot: <h1>What should we do?</h1>,
|
|
4106
|
-
afterComposerSlot: <div data-testid="home-app-grid" />,
|
|
4107
|
-
suggestions: ["Explore my apps"],
|
|
4108
|
-
})}
|
|
4109
|
-
/>,
|
|
4110
|
-
);
|
|
4197
|
+
const restoredProps = baseProps({
|
|
4198
|
+
threadId,
|
|
4199
|
+
browserTabId,
|
|
4200
|
+
isNewThread: false,
|
|
4201
|
+
onSaveThread: savedSnapshots,
|
|
4202
|
+
centerComposerWhenEmpty: true,
|
|
4203
|
+
suggestionPlacement: "context-chips",
|
|
4204
|
+
homeIntroSlot: <h1>What should we do?</h1>,
|
|
4205
|
+
afterComposerSlot: <div data-testid="home-app-grid" />,
|
|
4206
|
+
suggestions: ["Explore my apps"],
|
|
4111
4207
|
});
|
|
4208
|
+
const restoredRef = createRef<any>();
|
|
4209
|
+
await act(async () =>
|
|
4210
|
+
root.render(
|
|
4211
|
+
<AgentKitAssistantChat {...restoredProps} ref={restoredRef} />,
|
|
4212
|
+
),
|
|
4213
|
+
);
|
|
4112
4214
|
|
|
4113
4215
|
expect(container.textContent).toContain("Keep this transcript visible");
|
|
4114
4216
|
expect(container.textContent).toContain(
|
|
@@ -4122,6 +4224,10 @@ describe("AgentKitAssistantChat host behavior", () => {
|
|
|
4122
4224
|
const handoff = await chatMocks.rootProps.transport.getThreadSnapshot({
|
|
4123
4225
|
threadId,
|
|
4124
4226
|
});
|
|
4227
|
+
expect(handoff).toMatchObject({
|
|
4228
|
+
title: "Keep this transcript visible",
|
|
4229
|
+
titleSource: "fallback",
|
|
4230
|
+
});
|
|
4125
4231
|
expect(handoff.messages[0].parts).toContainEqual({
|
|
4126
4232
|
type: "text",
|
|
4127
4233
|
text: "Keep this transcript visible",
|
|
@@ -4131,6 +4237,22 @@ describe("AgentKitAssistantChat host behavior", () => {
|
|
|
4131
4237
|
text: "Assistant answer survives handoff",
|
|
4132
4238
|
});
|
|
4133
4239
|
expect(fetch).not.toHaveBeenCalled();
|
|
4240
|
+
|
|
4241
|
+
chatMocks.thread = {
|
|
4242
|
+
...chatMocks.thread,
|
|
4243
|
+
thread: handoff,
|
|
4244
|
+
messages: [...handoff.messages, followupUserMessage],
|
|
4245
|
+
};
|
|
4246
|
+
await act(async () =>
|
|
4247
|
+
root.render(
|
|
4248
|
+
<AgentKitAssistantChat {...restoredProps} ref={restoredRef} />,
|
|
4249
|
+
),
|
|
4250
|
+
);
|
|
4251
|
+
const restoredSurfaceSnapshot = restoredRef.current?.exportThreadSnapshot();
|
|
4252
|
+
expect(restoredSurfaceSnapshot).toMatchObject({
|
|
4253
|
+
title: "Keep this transcript visible",
|
|
4254
|
+
titleSource: "fallback",
|
|
4255
|
+
});
|
|
4134
4256
|
});
|
|
4135
4257
|
|
|
4136
4258
|
it("keeps transient thread-restore errors retryable without clearing the tab", async () => {
|
|
@@ -83,9 +83,12 @@ import {
|
|
|
83
83
|
realtimeVoiceTranscriptRegistry,
|
|
84
84
|
type RealtimeVoiceTranscriptMessage,
|
|
85
85
|
} from "@agent-native/toolkit/composer/realtime-voice-transcript";
|
|
86
|
+
import { IconButton } from "@agent-native/toolkit/design-system";
|
|
86
87
|
import { cn } from "@agent-native/toolkit/utils";
|
|
87
88
|
import {
|
|
88
89
|
IconAlertTriangle,
|
|
90
|
+
IconCircleCheck,
|
|
91
|
+
IconCopy,
|
|
89
92
|
IconLoader2,
|
|
90
93
|
IconMessage,
|
|
91
94
|
IconPlayerStopFilled,
|
|
@@ -169,7 +172,10 @@ import {
|
|
|
169
172
|
} from "./chat/tool-call-display.js";
|
|
170
173
|
import { resolveAgentKitToolSource } from "./chat/tool-integration.js";
|
|
171
174
|
import { ExternalAgentNudge } from "./external-agent-host.js";
|
|
175
|
+
import { fallbackChatTitle } from "./fallback-chat-title.js";
|
|
176
|
+
import { formatFeedbackReport } from "./feedback-report.js";
|
|
172
177
|
import { FileStorageSetupPopover } from "./FileStorageSetupPopover.js";
|
|
178
|
+
import { reconcileSettledRun } from "./reconcile-settled-run.js";
|
|
173
179
|
import { RunStuckBanner } from "./RunStuckBanner.js";
|
|
174
180
|
import { ThinkingDisplayProvider } from "./thinking-display.js";
|
|
175
181
|
|
|
@@ -200,9 +206,12 @@ const DEFERRED_PROVIDER_SUBMISSION_CLAIM_TTL_MS = 15 * 60 * 1000;
|
|
|
200
206
|
const DEFERRED_PROVIDER_SUBMISSION_MAX_RETRIES = 3;
|
|
201
207
|
const DEFERRED_PROVIDER_SUBMISSIONS_KEY_PREFIX =
|
|
202
208
|
"agentkit-deferred-provider-submissions:";
|
|
209
|
+
type AgentKitHandoffThreadSnapshot = AgentThreadSnapshot & {
|
|
210
|
+
titleSource?: "fallback";
|
|
211
|
+
};
|
|
203
212
|
const threadHandoffSnapshots = new Map<
|
|
204
213
|
string,
|
|
205
|
-
{ snapshot:
|
|
214
|
+
{ snapshot: AgentKitHandoffThreadSnapshot; expiresAt: number }
|
|
206
215
|
>();
|
|
207
216
|
const deferredProviderSubmissionOperations = new Map<
|
|
208
217
|
string,
|
|
@@ -856,6 +865,11 @@ export const AgentKitAssistantChat = forwardRef<
|
|
|
856
865
|
shortcut: "{{shortcut}}",
|
|
857
866
|
}),
|
|
858
867
|
feedbackSubmit: t("agentChat.feedback.submit"),
|
|
868
|
+
feedbackReasonMisread: t("agentChat.feedback.reasonMisread"),
|
|
869
|
+
feedbackReasonNotDone: t("agentChat.feedback.reasonNotDone"),
|
|
870
|
+
feedbackReasonWrongNumbers: t("agentChat.feedback.reasonWrongNumbers"),
|
|
871
|
+
feedbackReasonTooSlow: t("agentChat.feedback.tooSlow"),
|
|
872
|
+
feedbackCopyDetails: t("agentChat.feedback.copyDetails"),
|
|
859
873
|
fork: t("agentChat.message.forkChat"),
|
|
860
874
|
previousBranch: t("agentChat.message.previousBranch"),
|
|
861
875
|
nextBranch: t("agentChat.message.nextBranch"),
|
|
@@ -863,6 +877,8 @@ export const AgentKitAssistantChat = forwardRef<
|
|
|
863
877
|
error: t("agentChat.error.failed"),
|
|
864
878
|
renderError: t("agentChat.error.render"),
|
|
865
879
|
runFailed: t("agentChat.error.failed"),
|
|
880
|
+
continueRun: t("agentChat.common.continue"),
|
|
881
|
+
continueRunUnavailable: t("agentChat.recovery.continueUnavailable"),
|
|
866
882
|
reconnect: t("agentChat.agentPanel.chatgptSubscriptionReconnect"),
|
|
867
883
|
reasoning: t("agentChat.status.thinking"),
|
|
868
884
|
expandActivity: t("agentChat.common.expand"),
|
|
@@ -1202,6 +1218,7 @@ export const AgentKitAssistantChat = forwardRef<
|
|
|
1202
1218
|
labels={labels}
|
|
1203
1219
|
branchNavigation={props.branchNavigation}
|
|
1204
1220
|
loadRunUsage={loadRunUsage}
|
|
1221
|
+
buildFeedbackReport={formatFeedbackReport}
|
|
1205
1222
|
onThreadForked={(thread) => props.onForkedThread?.(thread.id)}
|
|
1206
1223
|
onCopyMessage={({ text }) => {
|
|
1207
1224
|
const html = renderMarkdownToClipboardHtml(text);
|
|
@@ -1522,6 +1539,15 @@ const AgentKitAssistantChatBody = forwardRef<
|
|
|
1522
1539
|
() => hasActiveAgentRuns(controller.getThread(threadId)),
|
|
1523
1540
|
[controller, threadId],
|
|
1524
1541
|
);
|
|
1542
|
+
const reconcileServerSettled = useCallback(
|
|
1543
|
+
() =>
|
|
1544
|
+
reconcileSettledRun({
|
|
1545
|
+
load: control.load,
|
|
1546
|
+
getThread: () => controller.getThread(threadId),
|
|
1547
|
+
tabId: props.tabId ?? threadId,
|
|
1548
|
+
}),
|
|
1549
|
+
[control.load, controller, props.tabId, threadId],
|
|
1550
|
+
);
|
|
1525
1551
|
|
|
1526
1552
|
useEffect(() => {
|
|
1527
1553
|
if (previousPrefillRevisionRef.current === prefillRevision) return;
|
|
@@ -3141,6 +3167,7 @@ const AgentKitAssistantChatBody = forwardRef<
|
|
|
3141
3167
|
)
|
|
3142
3168
|
}
|
|
3143
3169
|
isAwaitingResponse={() => isRunning}
|
|
3170
|
+
onServerSettled={reconcileServerSettled}
|
|
3144
3171
|
onRetry={() =>
|
|
3145
3172
|
void sendRecoveryMessage(RECOVERY_CONTINUE_PROMPT, "continue")
|
|
3146
3173
|
}
|
|
@@ -3369,6 +3396,32 @@ function AgentKitTranscript({ children, threadId }: AgentKitRegionRenderProps) {
|
|
|
3369
3396
|
const threadMessageIds = new Set(
|
|
3370
3397
|
thread.messages.map((message) => message.id),
|
|
3371
3398
|
);
|
|
3399
|
+
const activeRunId = thread.activeRunIds.at(-1);
|
|
3400
|
+
const [requestIdCopyFeedback, setRequestIdCopyFeedback] = useState<{
|
|
3401
|
+
runId: string;
|
|
3402
|
+
status: "copied" | "failed";
|
|
3403
|
+
} | null>(null);
|
|
3404
|
+
const activeRunRequestIdCopyStatus =
|
|
3405
|
+
requestIdCopyFeedback && requestIdCopyFeedback.runId === activeRunId
|
|
3406
|
+
? requestIdCopyFeedback.status
|
|
3407
|
+
: null;
|
|
3408
|
+
useEffect(() => {
|
|
3409
|
+
if (!requestIdCopyFeedback) return;
|
|
3410
|
+
const timeout = setTimeout(() => setRequestIdCopyFeedback(null), 1_400);
|
|
3411
|
+
return () => clearTimeout(timeout);
|
|
3412
|
+
}, [requestIdCopyFeedback]);
|
|
3413
|
+
const copyActiveRunRequestId = async () => {
|
|
3414
|
+
if (!activeRunId) return;
|
|
3415
|
+
try {
|
|
3416
|
+
const copied = await writeClipboardText(activeRunId);
|
|
3417
|
+
setRequestIdCopyFeedback({
|
|
3418
|
+
runId: activeRunId,
|
|
3419
|
+
status: copied ? "copied" : "failed",
|
|
3420
|
+
});
|
|
3421
|
+
} catch {
|
|
3422
|
+
setRequestIdCopyFeedback({ runId: activeRunId, status: "failed" });
|
|
3423
|
+
}
|
|
3424
|
+
};
|
|
3372
3425
|
const lastMessage = thread.messages.at(-1);
|
|
3373
3426
|
const showThinking =
|
|
3374
3427
|
surface.isSubmissionInFlight &&
|
|
@@ -3538,6 +3591,46 @@ function AgentKitTranscript({ children, threadId }: AgentKitRegionRenderProps) {
|
|
|
3538
3591
|
surface.props.tabId,
|
|
3539
3592
|
)}
|
|
3540
3593
|
{children}
|
|
3594
|
+
{activeRunId ? (
|
|
3595
|
+
<div
|
|
3596
|
+
className="agentkit-activities-static"
|
|
3597
|
+
data-agentkit-active-run-id-copy="true"
|
|
3598
|
+
>
|
|
3599
|
+
<IconButton
|
|
3600
|
+
label={
|
|
3601
|
+
activeRunRequestIdCopyStatus === "copied"
|
|
3602
|
+
? t("agentChat.common.copied")
|
|
3603
|
+
: activeRunRequestIdCopyStatus === "failed"
|
|
3604
|
+
? t("agentChat.recovery.copyFailed")
|
|
3605
|
+
: t("agentChat.message.copyRequestId")
|
|
3606
|
+
}
|
|
3607
|
+
title={t("agentChat.message.copyRequestId")}
|
|
3608
|
+
icon={
|
|
3609
|
+
activeRunRequestIdCopyStatus === "copied" ? (
|
|
3610
|
+
<IconCircleCheck aria-hidden="true" />
|
|
3611
|
+
) : activeRunRequestIdCopyStatus === "failed" ? (
|
|
3612
|
+
<IconAlertTriangle aria-hidden="true" />
|
|
3613
|
+
) : (
|
|
3614
|
+
<IconCopy aria-hidden="true" />
|
|
3615
|
+
)
|
|
3616
|
+
}
|
|
3617
|
+
size="compact"
|
|
3618
|
+
onPress={() => void copyActiveRunRequestId()}
|
|
3619
|
+
/>
|
|
3620
|
+
{activeRunRequestIdCopyStatus ? (
|
|
3621
|
+
<span
|
|
3622
|
+
className="sr-only"
|
|
3623
|
+
role={
|
|
3624
|
+
activeRunRequestIdCopyStatus === "failed" ? "alert" : "status"
|
|
3625
|
+
}
|
|
3626
|
+
>
|
|
3627
|
+
{activeRunRequestIdCopyStatus === "copied"
|
|
3628
|
+
? t("agentChat.common.copied")
|
|
3629
|
+
: t("agentChat.recovery.copyFailed")}
|
|
3630
|
+
</span>
|
|
3631
|
+
) : null}
|
|
3632
|
+
</div>
|
|
3633
|
+
) : null}
|
|
3541
3634
|
{handoffMessages.map((message) => (
|
|
3542
3635
|
<AgentMessageView
|
|
3543
3636
|
key={message.id}
|
|
@@ -4607,6 +4700,7 @@ function AgentKitRunFailure({
|
|
|
4607
4700
|
const surface = useAgentKitSurface();
|
|
4608
4701
|
const t = useT();
|
|
4609
4702
|
const [dismissed, setDismissed] = useState<string | null>(null);
|
|
4703
|
+
const [continueFailed, setContinueFailed] = useState(false);
|
|
4610
4704
|
const authErrorReason =
|
|
4611
4705
|
error.code === "unauthorized" || error.code === "http_401"
|
|
4612
4706
|
? "session-expired"
|
|
@@ -4690,6 +4784,17 @@ function AgentKitRunFailure({
|
|
|
4690
4784
|
);
|
|
4691
4785
|
}
|
|
4692
4786
|
if (wasRetried(thread.messages, runId)) return null;
|
|
4787
|
+
// Continuing resumes the stopped run's own turn, so finished steps are not
|
|
4788
|
+
// run again; that needs it to still be the turn's newest run.
|
|
4789
|
+
const continueStoppedRun = control.canContinueRun
|
|
4790
|
+
? superseded
|
|
4791
|
+
? undefined
|
|
4792
|
+
: () => {
|
|
4793
|
+
setContinueFailed(false);
|
|
4794
|
+
control.continueRun(runId).catch(() => setContinueFailed(true));
|
|
4795
|
+
}
|
|
4796
|
+
: () =>
|
|
4797
|
+
void surface.sendRecoveryMessage(RECOVERY_CONTINUE_PROMPT, "continue");
|
|
4693
4798
|
const info: RunErrorInfo = {
|
|
4694
4799
|
message: formatAgentKitErrorText(error, t),
|
|
4695
4800
|
errorCode: error.code,
|
|
@@ -4700,8 +4805,9 @@ function AgentKitRunFailure({
|
|
|
4700
4805
|
return (
|
|
4701
4806
|
<RunErrorRecoveryCard
|
|
4702
4807
|
info={info}
|
|
4703
|
-
onContinue={
|
|
4704
|
-
|
|
4808
|
+
onContinue={continueStoppedRun}
|
|
4809
|
+
continueError={
|
|
4810
|
+
continueFailed ? t("agentChat.recovery.continueUnavailable") : null
|
|
4705
4811
|
}
|
|
4706
4812
|
onRetry={() => void retryFailedTurn()}
|
|
4707
4813
|
retryHasUnavailableAttachment={retryRequest.hasUnavailableAttachment}
|
|
@@ -5334,7 +5440,13 @@ function createAgentKitThreadSnapshot(thread: AgentThreadState) {
|
|
|
5334
5440
|
agentMessageText(
|
|
5335
5441
|
[...messages].reverse().find((message) => message.role === "user")!,
|
|
5336
5442
|
);
|
|
5337
|
-
const
|
|
5443
|
+
const handoffThread = thread.thread as AgentKitHandoffThreadSnapshot | null;
|
|
5444
|
+
const savedTitle = handoffThread?.title?.trim();
|
|
5445
|
+
const titleSource: "fallback" | undefined =
|
|
5446
|
+
!savedTitle || handoffThread?.titleSource === "fallback"
|
|
5447
|
+
? "fallback"
|
|
5448
|
+
: undefined;
|
|
5449
|
+
const title = savedTitle || fallbackChatTitle(firstUserText ?? "");
|
|
5338
5450
|
const runs = Object.entries(thread.runs).map(([id, run]) => ({
|
|
5339
5451
|
...run,
|
|
5340
5452
|
id,
|
|
@@ -5356,6 +5468,7 @@ function createAgentKitThreadSnapshot(thread: AgentThreadState) {
|
|
|
5356
5468
|
agentKit,
|
|
5357
5469
|
}),
|
|
5358
5470
|
title,
|
|
5471
|
+
...(titleSource ? { titleSource } : {}),
|
|
5359
5472
|
preview: (latestUserText ?? "").slice(0, 280),
|
|
5360
5473
|
messageCount: messages.length,
|
|
5361
5474
|
};
|
|
@@ -5389,7 +5502,7 @@ function createAgentKitThreadHandoffKey(
|
|
|
5389
5502
|
function readAgentKitThreadHandoffSnapshot(
|
|
5390
5503
|
key: string,
|
|
5391
5504
|
consume = false,
|
|
5392
|
-
):
|
|
5505
|
+
): AgentKitHandoffThreadSnapshot | null {
|
|
5393
5506
|
const entry = threadHandoffSnapshots.get(key);
|
|
5394
5507
|
if (!entry) return null;
|
|
5395
5508
|
if (entry.expiresAt <= Date.now()) {
|
|
@@ -5413,10 +5526,13 @@ function storeAgentKitThreadHandoffSnapshot(
|
|
|
5413
5526
|
if (!messages.length) return;
|
|
5414
5527
|
|
|
5415
5528
|
const now = new Date().toISOString();
|
|
5416
|
-
const handoff:
|
|
5529
|
+
const handoff: AgentKitHandoffThreadSnapshot = {
|
|
5417
5530
|
...(thread.thread ?? {}),
|
|
5418
5531
|
id: thread.id,
|
|
5419
5532
|
title: snapshot.title || thread.thread?.title,
|
|
5533
|
+
...(snapshot.titleSource === "fallback"
|
|
5534
|
+
? { titleSource: "fallback" as const }
|
|
5535
|
+
: {}),
|
|
5420
5536
|
createdAt: thread.thread?.createdAt ?? messages[0]?.createdAt ?? now,
|
|
5421
5537
|
updatedAt: thread.thread?.updatedAt ?? messages.at(-1)?.createdAt ?? now,
|
|
5422
5538
|
messages,
|
|
@@ -5617,7 +5733,7 @@ function appendVoiceTranscriptsToThreadSnapshot(
|
|
|
5617
5733
|
}),
|
|
5618
5734
|
title:
|
|
5619
5735
|
snapshot.title ||
|
|
5620
|
-
(firstUser ? agentMessageText(firstUser)
|
|
5736
|
+
(firstUser ? fallbackChatTitle(agentMessageText(firstUser)) : ""),
|
|
5621
5737
|
preview: latestUser ? agentMessageText(latestUser).slice(0, 280) : "",
|
|
5622
5738
|
messageCount: messages.length,
|
|
5623
5739
|
};
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from "@agent-native/toolkit/ui/dropdown-menu";
|
|
10
10
|
import React, { act } from "react";
|
|
11
11
|
import { createRoot, type Root } from "react-dom/client";
|
|
12
|
-
import { MemoryRouter, useLocation } from "react-router";
|
|
12
|
+
import { MemoryRouter, useHref, useLocation } from "react-router";
|
|
13
13
|
import { describe, expect, it, vi } from "vitest";
|
|
14
14
|
|
|
15
15
|
import {
|
|
@@ -445,6 +445,45 @@ describe("AgentPanel settings navigation", () => {
|
|
|
445
445
|
}
|
|
446
446
|
});
|
|
447
447
|
|
|
448
|
+
it("keeps a settings request inside the mounted app path", () => {
|
|
449
|
+
const container = document.createElement("div");
|
|
450
|
+
document.body.appendChild(container);
|
|
451
|
+
const root: Root = createRoot(container);
|
|
452
|
+
let href = "";
|
|
453
|
+
|
|
454
|
+
function LocationProbe() {
|
|
455
|
+
const location = useLocation();
|
|
456
|
+
href = useHref(`${location.pathname}${location.hash}`);
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
try {
|
|
461
|
+
vi.stubEnv("VITE_APP_BASE_PATH", "/dispatch");
|
|
462
|
+
act(() => {
|
|
463
|
+
window.history.replaceState(null, "", "/dispatch/chat#llm");
|
|
464
|
+
root.render(
|
|
465
|
+
React.createElement(
|
|
466
|
+
MemoryRouter,
|
|
467
|
+
{ basename: "/dispatch", initialEntries: ["/dispatch/chat#llm"] },
|
|
468
|
+
React.createElement(AgentPanelSettingsNavigation),
|
|
469
|
+
React.createElement(LocationProbe),
|
|
470
|
+
),
|
|
471
|
+
);
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
act(() => {
|
|
475
|
+
window.dispatchEvent(new CustomEvent("agent-panel:open-settings"));
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
expect(href).toBe("/dispatch/settings#llm");
|
|
479
|
+
} finally {
|
|
480
|
+
act(() => root.unmount());
|
|
481
|
+
container.remove();
|
|
482
|
+
window.history.replaceState(null, "", "/");
|
|
483
|
+
vi.unstubAllEnvs();
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
|
|
448
487
|
it("routes a mounted settings request to an existing secret-specific hash", () => {
|
|
449
488
|
const container = document.createElement("div");
|
|
450
489
|
document.body.appendChild(container);
|
|
@@ -532,7 +571,7 @@ describe("AgentPanel settings navigation", () => {
|
|
|
532
571
|
}
|
|
533
572
|
});
|
|
534
573
|
|
|
535
|
-
it("
|
|
574
|
+
it("navigates with a router-local settings path", () => {
|
|
536
575
|
const container = document.createElement("div");
|
|
537
576
|
document.body.appendChild(container);
|
|
538
577
|
const root: Root = createRoot(container);
|
|
@@ -545,11 +584,10 @@ describe("AgentPanel settings navigation", () => {
|
|
|
545
584
|
|
|
546
585
|
try {
|
|
547
586
|
act(() => {
|
|
548
|
-
window.history.replaceState(null, "", "/dispatch/_agent-native/poll");
|
|
549
587
|
root.render(
|
|
550
588
|
React.createElement(
|
|
551
589
|
MemoryRouter,
|
|
552
|
-
{ initialEntries: ["/"] },
|
|
590
|
+
{ initialEntries: ["/chat"] },
|
|
553
591
|
React.createElement(AgentPanelSettingsNavigation),
|
|
554
592
|
React.createElement(LocationProbe),
|
|
555
593
|
),
|
|
@@ -564,11 +602,10 @@ describe("AgentPanel settings navigation", () => {
|
|
|
564
602
|
);
|
|
565
603
|
});
|
|
566
604
|
|
|
567
|
-
expect(pathname).toBe("/
|
|
605
|
+
expect(pathname).toBe("/settings");
|
|
568
606
|
} finally {
|
|
569
607
|
act(() => root.unmount());
|
|
570
608
|
container.remove();
|
|
571
|
-
window.history.replaceState(null, "", "/");
|
|
572
609
|
}
|
|
573
610
|
});
|
|
574
611
|
|
|
@@ -1134,7 +1171,6 @@ describe("AgentChatSurface chrome defaults", () => {
|
|
|
1134
1171
|
expect(source).not.toContain("SettingsPanel");
|
|
1135
1172
|
expect(source).not.toContain("allowSettingsMode");
|
|
1136
1173
|
expect(source).not.toContain('mode === "settings"');
|
|
1137
|
-
expect(source).toContain('pathname: appPath("/settings")');
|
|
1138
1174
|
});
|
|
1139
1175
|
|
|
1140
1176
|
it("mounts URL command sync for a full-page chat surface", () => {
|
|
@@ -199,7 +199,7 @@ export function AgentPanelSettingsNavigation({
|
|
|
199
199
|
const requested = requestedSettingsSection(section, window.location.hash);
|
|
200
200
|
const navigation = navigate(
|
|
201
201
|
{
|
|
202
|
-
pathname:
|
|
202
|
+
pathname: "/settings",
|
|
203
203
|
hash: settingsRouteHashForSection(section, window.location.hash),
|
|
204
204
|
},
|
|
205
205
|
// The hash can't tell API keys from Integrations; the redesigned
|
|
@@ -85,6 +85,13 @@ const assistantChatMockState = vi.hoisted(() => ({
|
|
|
85
85
|
onRetryModelList: undefined as (() => void) | undefined,
|
|
86
86
|
onSlashCommand: undefined as ((command: string) => void) | undefined,
|
|
87
87
|
onForkedThread: undefined as ((threadId: string) => void) | undefined,
|
|
88
|
+
onGenerateTitle: undefined as
|
|
89
|
+
| ((
|
|
90
|
+
threadId: string,
|
|
91
|
+
message: string,
|
|
92
|
+
selection: { engine?: string; model?: string },
|
|
93
|
+
) => void)
|
|
94
|
+
| undefined,
|
|
88
95
|
onSaveThread: undefined as
|
|
89
96
|
| ((
|
|
90
97
|
threadId: string,
|
|
@@ -93,6 +100,7 @@ const assistantChatMockState = vi.hoisted(() => ({
|
|
|
93
100
|
title: string;
|
|
94
101
|
preview: string;
|
|
95
102
|
messageCount: number;
|
|
103
|
+
titleSource?: "fallback";
|
|
96
104
|
},
|
|
97
105
|
) => void)
|
|
98
106
|
| undefined,
|
|
@@ -351,6 +359,7 @@ vi.mock("./AgentKitAssistantChat.js", async () => {
|
|
|
351
359
|
onRetryModelList?: () => void;
|
|
352
360
|
onSlashCommand?: (command: string) => void;
|
|
353
361
|
onForkedThread?: (threadId: string) => void;
|
|
362
|
+
onGenerateTitle?: typeof assistantChatMockState.onGenerateTitle;
|
|
354
363
|
onSaveThread?: typeof assistantChatMockState.onSaveThread;
|
|
355
364
|
branchNavigation?: typeof assistantChatMockState.branchNavigation;
|
|
356
365
|
};
|
|
@@ -359,6 +368,7 @@ vi.mock("./AgentKitAssistantChat.js", async () => {
|
|
|
359
368
|
assistantChatMockState.onRetryModelList = props.onRetryModelList;
|
|
360
369
|
assistantChatMockState.onSlashCommand = props.onSlashCommand;
|
|
361
370
|
assistantChatMockState.onForkedThread = props.onForkedThread;
|
|
371
|
+
assistantChatMockState.onGenerateTitle = props.onGenerateTitle;
|
|
362
372
|
assistantChatMockState.onSaveThread = props.onSaveThread;
|
|
363
373
|
assistantChatMockState.branchNavigation = props.branchNavigation;
|
|
364
374
|
React.useImperativeHandle(ref, () => ({
|
|
@@ -413,6 +423,7 @@ function resetThreadMocks() {
|
|
|
413
423
|
assistantChatMockState.onRetryModelList = undefined;
|
|
414
424
|
assistantChatMockState.onSlashCommand = undefined;
|
|
415
425
|
assistantChatMockState.onForkedThread = undefined;
|
|
426
|
+
assistantChatMockState.onGenerateTitle = undefined;
|
|
416
427
|
assistantChatMockState.onSaveThread = undefined;
|
|
417
428
|
assistantChatMockState.branchNavigation = undefined;
|
|
418
429
|
threadMocks.activeThreadId = "thread-1";
|
|
@@ -522,6 +533,7 @@ describe("MultiTabAssistantChat postMessage bridge", () => {
|
|
|
522
533
|
title: "Saved chat",
|
|
523
534
|
preview: "Latest request",
|
|
524
535
|
messageCount: 1,
|
|
536
|
+
titleSource: "fallback" as const,
|
|
525
537
|
};
|
|
526
538
|
window.history.replaceState(null, "", "/");
|
|
527
539
|
|
|
@@ -583,6 +595,28 @@ describe("MultiTabAssistantChat postMessage bridge", () => {
|
|
|
583
595
|
expect(window.location.search).toBe("?thread=thread-1");
|
|
584
596
|
});
|
|
585
597
|
|
|
598
|
+
it("persists a sanitized prompt title when title generation is unavailable", async () => {
|
|
599
|
+
threadMocks.generateTitle.mockResolvedValueOnce(null);
|
|
600
|
+
await act(async () => {
|
|
601
|
+
root.render(<MultiTabAssistantChat storageKey="title-fallback" />);
|
|
602
|
+
await Promise.resolve();
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
const message =
|
|
606
|
+
'Summarize @[the sprint|resource:123]\n<context data-agentkit-context-encoding="entities-v1">Private context</context>';
|
|
607
|
+
await act(async () => {
|
|
608
|
+
assistantChatMockState.onGenerateTitle?.("thread-1", message, {});
|
|
609
|
+
await Promise.resolve();
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
expect(threadMocks.saveThreadData).toHaveBeenCalledWith("thread-1", {
|
|
613
|
+
threadData: "",
|
|
614
|
+
title: "Summarize @the sprint",
|
|
615
|
+
preview: message.slice(0, 120),
|
|
616
|
+
titleSource: "fallback",
|
|
617
|
+
});
|
|
618
|
+
});
|
|
619
|
+
|
|
586
620
|
it("prefills the active composer without submitting when submit is false", () => {
|
|
587
621
|
act(() => {
|
|
588
622
|
dispatchSubmitChat({
|