@agent-native/toolkit 0.205.0 → 0.206.0-nightly-20261009011246
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.map +1 -1
- package/dist/app/agentkit/react/components.js +17 -9
- package/dist/app/agentkit/react/components.js.map +1 -1
- package/dist/app/auth/AuthPage.d.ts.map +1 -1
- package/dist/app/auth/AuthPage.js +11 -0
- package/dist/app/auth/AuthPage.js.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.d.ts +2 -0
- package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.js +24 -14
- package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
- package/dist/app/chat/AgentPanel.d.ts.map +1 -1
- package/dist/app/chat/AgentPanel.js +3 -2
- package/dist/app/chat/AgentPanel.js.map +1 -1
- package/dist/app/chat/MultiTabAssistantChat.d.ts +4 -1
- package/dist/app/chat/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/MultiTabAssistantChat.js +61 -3
- package/dist/app/chat/MultiTabAssistantChat.js.map +1 -1
- package/dist/app/chat/agentkit-chat/approval-card.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/approval-card.js +2 -1
- package/dist/app/chat/agentkit-chat/approval-card.js.map +1 -1
- package/dist/app/chat/chat/run-recovery.d.ts.map +1 -1
- package/dist/app/chat/chat/run-recovery.js +74 -8
- package/dist/app/chat/chat/run-recovery.js.map +1 -1
- package/dist/app/chat/chat/tool-call-display.d.ts.map +1 -1
- package/dist/app/chat/chat/tool-call-display.js +6 -4
- package/dist/app/chat/chat/tool-call-display.js.map +1 -1
- package/dist/app/chat/chat-first/ChatFirstAgentActivityPanel.d.ts.map +1 -1
- package/dist/app/chat/chat-first/ChatFirstAgentActivityPanel.js +2 -1
- package/dist/app/chat/chat-first/ChatFirstAgentActivityPanel.js.map +1 -1
- package/dist/app/chat/conversation/AgentConversation.d.ts.map +1 -1
- package/dist/app/chat/conversation/AgentConversation.js +10 -7
- package/dist/app/chat/conversation/AgentConversation.js.map +1 -1
- package/dist/app/chat/fallback-chat-title.d.ts.map +1 -1
- package/dist/app/chat/fallback-chat-title.js +2 -4
- package/dist/app/chat/fallback-chat-title.js.map +1 -1
- package/dist/app/chat/mcp-apps/McpAppRenderer.d.ts.map +1 -1
- package/dist/app/chat/mcp-apps/McpAppRenderer.js +3 -2
- package/dist/app/chat/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/app/chat/session-replay-privacy.d.ts +7 -0
- package/dist/app/chat/session-replay-privacy.d.ts.map +1 -0
- package/dist/app/chat/session-replay-privacy.js +6 -0
- package/dist/app/chat/session-replay-privacy.js.map +1 -0
- package/dist/app/extensions/ExtensionViewer.d.ts +4 -1
- package/dist/app/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/app/extensions/ExtensionViewer.js +3 -3
- package/dist/app/extensions/ExtensionViewer.js.map +1 -1
- package/dist/app/extensions/ExtensionViewerPage.d.ts +4 -1
- package/dist/app/extensions/ExtensionViewerPage.d.ts.map +1 -1
- package/dist/app/extensions/ExtensionViewerPage.js +2 -2
- package/dist/app/extensions/ExtensionViewerPage.js.map +1 -1
- package/dist/app/onboarding/FirstRunOnboarding.d.ts.map +1 -1
- package/dist/app/onboarding/FirstRunOnboarding.js +53 -12
- package/dist/app/onboarding/FirstRunOnboarding.js.map +1 -1
- package/dist/app/onboarding/OnboardingPanel.d.ts.map +1 -1
- package/dist/app/onboarding/OnboardingPanel.js +14 -4
- package/dist/app/onboarding/OnboardingPanel.js.map +1 -1
- package/dist/app/resources/ResourceEditor.d.ts +4 -1
- package/dist/app/resources/ResourceEditor.d.ts.map +1 -1
- package/dist/app/resources/ResourceEditor.js +18 -7
- package/dist/app/resources/ResourceEditor.js.map +1 -1
- package/dist/app/resources/ResourcesPanel.d.ts +4 -1
- package/dist/app/resources/ResourcesPanel.d.ts.map +1 -1
- package/dist/app/resources/ResourcesPanel.js +18 -18
- package/dist/app/resources/ResourcesPanel.js.map +1 -1
- package/dist/app/resources/custom-agent-model-options.d.ts +16 -0
- package/dist/app/resources/custom-agent-model-options.d.ts.map +1 -0
- package/dist/app/resources/custom-agent-model-options.js +53 -0
- package/dist/app/resources/custom-agent-model-options.js.map +1 -0
- package/dist/app/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/app/settings/SettingsPanel.js +34 -3
- package/dist/app/settings/SettingsPanel.js.map +1 -1
- package/dist/app/settings/SettingsTabsPage.d.ts.map +1 -1
- package/dist/app/settings/SettingsTabsPage.js +2 -0
- package/dist/app/settings/SettingsTabsPage.js.map +1 -1
- package/dist/app/settings/api-keys/ApiKeyDialogs.d.ts.map +1 -1
- package/dist/app/settings/api-keys/ApiKeyDialogs.js +49 -9
- package/dist/app/settings/api-keys/ApiKeyDialogs.js.map +1 -1
- package/dist/app/settings/api-keys/ApiKeysSettingsPage.js +2 -1
- package/dist/app/settings/api-keys/ApiKeysSettingsPage.js.map +1 -1
- package/dist/app/settings/model/ModelSettingsPage.d.ts.map +1 -1
- package/dist/app/settings/model/ModelSettingsPage.js +43 -9
- package/dist/app/settings/model/ModelSettingsPage.js.map +1 -1
- package/dist/app/settings/model/ProviderDialog.d.ts +4 -0
- package/dist/app/settings/model/ProviderDialog.d.ts.map +1 -1
- package/dist/app/settings/model/ProviderDialog.js +107 -11
- package/dist/app/settings/model/ProviderDialog.js.map +1 -1
- package/dist/app/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/app/settings/useBuilderStatus.js +13 -10
- package/dist/app/settings/useBuilderStatus.js.map +1 -1
- package/dist/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/composer/TiptapComposer.js +51 -13
- package/dist/composer/TiptapComposer.js.map +1 -1
- package/package.json +4 -4
- package/src/app/agentkit/react/components.interaction.spec.tsx +229 -1
- package/src/app/agentkit/react/components.spec.tsx +42 -1
- package/src/app/agentkit/react/components.tsx +46 -12
- package/src/app/auth/AuthPage.tsx +13 -0
- package/src/app/chat/AgentKitAssistantChat.spec.tsx +116 -5
- package/src/app/chat/AgentKitAssistantChat.tsx +41 -20
- package/src/app/chat/AgentPanel.tsx +5 -1
- package/src/app/chat/MultiTabAssistantChat.spec.tsx +152 -7
- package/src/app/chat/MultiTabAssistantChat.tsx +71 -4
- package/src/app/chat/agentkit-chat/approval-card.spec.tsx +17 -0
- package/src/app/chat/agentkit-chat/approval-card.tsx +2 -0
- package/src/app/chat/chat/run-recovery.spec.tsx +354 -9
- package/src/app/chat/chat/run-recovery.tsx +178 -10
- package/src/app/chat/chat/tool-call-display.spec.tsx +77 -0
- package/src/app/chat/chat/tool-call-display.tsx +23 -6
- package/src/app/chat/chat-first/ChatFirstAgentActivityPanel.tsx +2 -1
- package/src/app/chat/conversation/AgentConversation.spec.tsx +190 -1
- package/src/app/chat/conversation/AgentConversation.tsx +32 -8
- package/src/app/chat/fallback-chat-title.spec.ts +44 -0
- package/src/app/chat/fallback-chat-title.ts +3 -4
- package/src/app/chat/mcp-apps/McpAppRenderer.spec.ts +24 -0
- package/src/app/chat/mcp-apps/McpAppRenderer.tsx +10 -2
- package/src/app/chat/session-replay-privacy.spec.ts +14 -0
- package/src/app/chat/session-replay-privacy.ts +5 -0
- package/src/app/extensions/ExtensionViewer.tsx +15 -3
- package/src/app/extensions/ExtensionViewerPage.tsx +7 -3
- package/src/app/onboarding/FirstRunOnboarding.spec.tsx +252 -2
- package/src/app/onboarding/FirstRunOnboarding.tsx +56 -9
- package/src/app/onboarding/OnboardingPanel.save-scope.spec.tsx +1 -0
- package/src/app/onboarding/OnboardingPanel.tsx +15 -3
- package/src/app/resources/ResourceEditor.spec.tsx +240 -5
- package/src/app/resources/ResourceEditor.tsx +57 -10
- package/src/app/resources/ResourcesPanel.spec.ts +86 -4
- package/src/app/resources/ResourcesPanel.tsx +49 -16
- package/src/app/resources/custom-agent-model-options.ts +92 -0
- package/src/app/settings/SettingsPanel.model-defaults.spec.tsx +26 -13
- package/src/app/settings/SettingsPanel.tsx +35 -3
- package/src/app/settings/SettingsTabsPage.spec.tsx +97 -0
- package/src/app/settings/SettingsTabsPage.tsx +2 -0
- package/src/app/settings/api-keys/ApiKeyDialogs.tsx +84 -22
- package/src/app/settings/api-keys/ApiKeysSettingsPage.spec.tsx +229 -1
- package/src/app/settings/api-keys/ApiKeysSettingsPage.tsx +3 -2
- package/src/app/settings/model/ModelSettingsPage.spec.tsx +99 -9
- package/src/app/settings/model/ModelSettingsPage.tsx +79 -11
- package/src/app/settings/model/ProviderDialog.spec.tsx +192 -1
- package/src/app/settings/model/ProviderDialog.tsx +225 -29
- package/src/app/settings/useBuilderStatus.spec.tsx +49 -0
- package/src/app/settings/useBuilderStatus.ts +16 -10
- package/src/composer/TiptapComposer.spec.ts +97 -2
- package/src/composer/TiptapComposer.tsx +67 -13
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
// @vitest-environment happy-dom
|
|
2
2
|
|
|
3
3
|
import { AgentNativeI18nProvider } from "@agent-native/core/client/i18n";
|
|
4
|
+
import { SESSION_REPLAY_BLOCK_ATTRIBUTE } from "@agent-native/core/client/session-replay-privacy";
|
|
4
5
|
import React, { act } from "react";
|
|
5
6
|
import { createRoot, type Root } from "react-dom/client";
|
|
6
7
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
7
8
|
|
|
8
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
clearToolRenderersForTests,
|
|
11
|
+
registerToolRenderer,
|
|
12
|
+
} from "../chat/tool-render-registry.js";
|
|
13
|
+
import {
|
|
14
|
+
AgentConversation,
|
|
15
|
+
AgentConversationMessageView,
|
|
16
|
+
} from "./AgentConversation.js";
|
|
17
|
+
|
|
18
|
+
vi.mock("../mcp-apps/McpAppRenderer.js", () => ({
|
|
19
|
+
McpAppRenderer: () => <div data-testid="conversation-mcp-app" />,
|
|
20
|
+
}));
|
|
9
21
|
|
|
10
22
|
vi.mock("../../extensions/index.js", () => ({
|
|
11
23
|
InlineExtensionFrame: ({ extensionId, extension }: any) => (
|
|
@@ -34,10 +46,187 @@ describe("AgentConversationMessageView", () => {
|
|
|
34
46
|
root.unmount();
|
|
35
47
|
});
|
|
36
48
|
container.remove();
|
|
49
|
+
clearToolRenderersForTests();
|
|
37
50
|
vi.restoreAllMocks();
|
|
38
51
|
vi.unstubAllGlobals();
|
|
39
52
|
});
|
|
40
53
|
|
|
54
|
+
it("masks the conversation error without masking its container", () => {
|
|
55
|
+
act(() =>
|
|
56
|
+
root.render(
|
|
57
|
+
<AgentConversation
|
|
58
|
+
messages={[]}
|
|
59
|
+
error="Example Person's example run failed."
|
|
60
|
+
/>,
|
|
61
|
+
),
|
|
62
|
+
);
|
|
63
|
+
const alert = container.querySelector('[role="alert"]');
|
|
64
|
+
expect(alert?.querySelector("span")?.hasAttribute("data-an-mask")).toBe(
|
|
65
|
+
true,
|
|
66
|
+
);
|
|
67
|
+
expect(alert?.hasAttribute("data-an-mask")).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it.each(["errored", "completed"] as const)(
|
|
71
|
+
"masks only errored conversation tool diagnostics (%s)",
|
|
72
|
+
(state) => {
|
|
73
|
+
act(() =>
|
|
74
|
+
root.render(
|
|
75
|
+
<AgentConversationMessageView
|
|
76
|
+
message={{
|
|
77
|
+
id: "message-example",
|
|
78
|
+
role: "assistant",
|
|
79
|
+
parts: [
|
|
80
|
+
{
|
|
81
|
+
id: "tool-example",
|
|
82
|
+
type: "tool",
|
|
83
|
+
tool: {
|
|
84
|
+
id: "tool-example",
|
|
85
|
+
name: "example-tool",
|
|
86
|
+
state,
|
|
87
|
+
summary: "Example Person's example notes.",
|
|
88
|
+
result: "Example Document failed.",
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
}}
|
|
93
|
+
/>,
|
|
94
|
+
),
|
|
95
|
+
);
|
|
96
|
+
const summary = container.querySelector(
|
|
97
|
+
".agent-conversation-tool__summary",
|
|
98
|
+
);
|
|
99
|
+
const result = container.querySelector("pre span");
|
|
100
|
+
expect(summary?.hasAttribute("data-an-mask")).toBe(state === "errored");
|
|
101
|
+
expect(result?.textContent).toBe("Example Document failed.");
|
|
102
|
+
expect(result?.hasAttribute("data-an-mask")).toBe(state === "errored");
|
|
103
|
+
expect(
|
|
104
|
+
container
|
|
105
|
+
.querySelector(".agent-conversation-tool__name")
|
|
106
|
+
?.closest("[data-an-mask]"),
|
|
107
|
+
).toBeNull();
|
|
108
|
+
expect(
|
|
109
|
+
container.querySelector("pre strong")?.closest("[data-an-mask]"),
|
|
110
|
+
).toBeNull();
|
|
111
|
+
},
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
it("shows an errored tool through the masked fallback, not its renderer", () => {
|
|
115
|
+
registerToolRenderer({
|
|
116
|
+
id: "example-renderer",
|
|
117
|
+
match: "example-tool",
|
|
118
|
+
Component: ({ context }) => (
|
|
119
|
+
<div data-testid="example-renderer">{context.resultText}</div>
|
|
120
|
+
),
|
|
121
|
+
});
|
|
122
|
+
act(() =>
|
|
123
|
+
root.render(
|
|
124
|
+
<AgentConversationMessageView
|
|
125
|
+
message={{
|
|
126
|
+
id: "message-example",
|
|
127
|
+
role: "assistant",
|
|
128
|
+
parts: [
|
|
129
|
+
{
|
|
130
|
+
id: "tool-example",
|
|
131
|
+
type: "tool",
|
|
132
|
+
tool: {
|
|
133
|
+
id: "tool-example",
|
|
134
|
+
name: "example-tool",
|
|
135
|
+
state: "errored",
|
|
136
|
+
result: "Example Document failed.",
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
}}
|
|
141
|
+
/>,
|
|
142
|
+
),
|
|
143
|
+
);
|
|
144
|
+
expect(container.querySelector('[data-testid="example-renderer"]')).toBe(
|
|
145
|
+
null,
|
|
146
|
+
);
|
|
147
|
+
expect(
|
|
148
|
+
container.querySelector("pre span")?.hasAttribute("data-an-mask"),
|
|
149
|
+
).toBe(true);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it.each(["errored", "completed"] as const)(
|
|
153
|
+
"blocks only an errored tool's MCP App from replays (%s)",
|
|
154
|
+
(state) => {
|
|
155
|
+
act(() =>
|
|
156
|
+
root.render(
|
|
157
|
+
<AgentConversationMessageView
|
|
158
|
+
message={{
|
|
159
|
+
id: "message-example",
|
|
160
|
+
role: "assistant",
|
|
161
|
+
parts: [
|
|
162
|
+
{
|
|
163
|
+
id: "tool-example",
|
|
164
|
+
type: "tool",
|
|
165
|
+
tool: {
|
|
166
|
+
id: "tool-example",
|
|
167
|
+
name: "example-tool",
|
|
168
|
+
state,
|
|
169
|
+
result: "Example Document failed.",
|
|
170
|
+
mcpApp: {
|
|
171
|
+
serverId: "server",
|
|
172
|
+
toolName: "example-tool",
|
|
173
|
+
originalToolName: "example-tool",
|
|
174
|
+
resourceUri: "ui://example-tool",
|
|
175
|
+
toolInput: {},
|
|
176
|
+
toolResult: {},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
}}
|
|
182
|
+
/>,
|
|
183
|
+
),
|
|
184
|
+
);
|
|
185
|
+
const app = container.querySelector(
|
|
186
|
+
'[data-testid="conversation-mcp-app"]',
|
|
187
|
+
);
|
|
188
|
+
expect(app).not.toBeNull();
|
|
189
|
+
expect(app?.closest(`[${SESSION_REPLAY_BLOCK_ATTRIBUTE}]`) !== null).toBe(
|
|
190
|
+
state === "errored",
|
|
191
|
+
);
|
|
192
|
+
},
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
it.each(["error", "warning", "info"] as const)(
|
|
196
|
+
"masks notice text but not its title or action (%s)",
|
|
197
|
+
(tone) => {
|
|
198
|
+
act(() =>
|
|
199
|
+
root.render(
|
|
200
|
+
<AgentConversationMessageView
|
|
201
|
+
message={{
|
|
202
|
+
id: "message-example",
|
|
203
|
+
role: "assistant",
|
|
204
|
+
notices: [
|
|
205
|
+
{
|
|
206
|
+
id: "notice-example",
|
|
207
|
+
tone,
|
|
208
|
+
title: "Run status",
|
|
209
|
+
text: "Example Person's example run status.",
|
|
210
|
+
action: <button>Retry</button>,
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
}}
|
|
214
|
+
/>,
|
|
215
|
+
),
|
|
216
|
+
);
|
|
217
|
+
const notice = container.querySelector(".agent-conversation-notice");
|
|
218
|
+
expect(notice?.querySelector("span")?.hasAttribute("data-an-mask")).toBe(
|
|
219
|
+
true,
|
|
220
|
+
);
|
|
221
|
+
expect(
|
|
222
|
+
notice?.querySelector("strong")?.closest("[data-an-mask]"),
|
|
223
|
+
).toBeNull();
|
|
224
|
+
expect(
|
|
225
|
+
notice?.querySelector("button")?.closest("[data-an-mask]"),
|
|
226
|
+
).toBeNull();
|
|
227
|
+
},
|
|
228
|
+
);
|
|
229
|
+
|
|
41
230
|
it("renders text and tool parts in transcript order", () => {
|
|
42
231
|
act(() => {
|
|
43
232
|
root.render(
|
|
@@ -34,6 +34,10 @@ import {
|
|
|
34
34
|
} from "../components/ui/message-scroller.js";
|
|
35
35
|
import { HighlightedCodeBlock as SharedHighlightedCodeBlock } from "../HighlightedCodeBlock.js";
|
|
36
36
|
import { McpAppRenderer } from "../mcp-apps/McpAppRenderer.js";
|
|
37
|
+
import {
|
|
38
|
+
SESSION_REPLAY_BLOCK_PROPS,
|
|
39
|
+
SESSION_REPLAY_MASK_PROPS,
|
|
40
|
+
} from "../session-replay-privacy.js";
|
|
37
41
|
import type {
|
|
38
42
|
AgentConversationAttachment,
|
|
39
43
|
AgentConversationArtifact,
|
|
@@ -70,7 +74,7 @@ export function AgentConversation({
|
|
|
70
74
|
{error && (
|
|
71
75
|
<div className="agent-conversation__error" role="alert">
|
|
72
76
|
<IconAlertTriangle size={15} strokeWidth={1.8} />
|
|
73
|
-
<span>{error}</span>
|
|
77
|
+
<span {...SESSION_REPLAY_MASK_PROPS}>{error}</span>
|
|
74
78
|
</div>
|
|
75
79
|
)}
|
|
76
80
|
<MessageScrollerProvider autoScroll>
|
|
@@ -412,9 +416,11 @@ function ConversationToolCall({ tool }: { tool: AgentConversationToolCall }) {
|
|
|
412
416
|
chatUI: tool.chatUI,
|
|
413
417
|
};
|
|
414
418
|
const NativeToolRenderer =
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
419
|
+
tool.state === "errored"
|
|
420
|
+
? null
|
|
421
|
+
: (resolveBuiltinActionChatRenderer(nativeToolContext) ??
|
|
422
|
+
resolveToolRenderer(nativeToolContext) ??
|
|
423
|
+
resolveBuiltinFallbackToolRenderer(nativeToolContext));
|
|
418
424
|
if (NativeToolRenderer) {
|
|
419
425
|
return (
|
|
420
426
|
<ActionChatUiSurface
|
|
@@ -446,7 +452,12 @@ function ConversationToolCall({ tool }: { tool: AgentConversationToolCall }) {
|
|
|
446
452
|
{toolLabel(t, tool.name)}
|
|
447
453
|
</span>
|
|
448
454
|
{tool.summary && (
|
|
449
|
-
<span
|
|
455
|
+
<span
|
|
456
|
+
{...(tool.state === "errored" ? SESSION_REPLAY_MASK_PROPS : {})}
|
|
457
|
+
className="agent-conversation-tool__summary"
|
|
458
|
+
>
|
|
459
|
+
{tool.summary}
|
|
460
|
+
</span>
|
|
450
461
|
)}
|
|
451
462
|
</>
|
|
452
463
|
);
|
|
@@ -468,7 +479,14 @@ function ConversationToolCall({ tool }: { tool: AgentConversationToolCall }) {
|
|
|
468
479
|
/>
|
|
469
480
|
</summary>
|
|
470
481
|
<div className="agent-conversation-tool__details">
|
|
471
|
-
{tool.mcpApp &&
|
|
482
|
+
{tool.mcpApp && (
|
|
483
|
+
// The recorder keeps iframe attributes, and a snapshot's srcdoc can show the failure.
|
|
484
|
+
<div
|
|
485
|
+
{...(tool.state === "errored" ? SESSION_REPLAY_BLOCK_PROPS : {})}
|
|
486
|
+
>
|
|
487
|
+
<McpAppRenderer app={tool.mcpApp} />
|
|
488
|
+
</div>
|
|
489
|
+
)}
|
|
472
490
|
{tool.input && (
|
|
473
491
|
<pre>
|
|
474
492
|
<strong>input</strong>
|
|
@@ -478,7 +496,11 @@ function ConversationToolCall({ tool }: { tool: AgentConversationToolCall }) {
|
|
|
478
496
|
{tool.result && (
|
|
479
497
|
<pre>
|
|
480
498
|
<strong>result</strong>
|
|
481
|
-
|
|
499
|
+
<span
|
|
500
|
+
{...(tool.state === "errored" ? SESSION_REPLAY_MASK_PROPS : {})}
|
|
501
|
+
>
|
|
502
|
+
{tool.result}
|
|
503
|
+
</span>
|
|
482
504
|
</pre>
|
|
483
505
|
)}
|
|
484
506
|
</div>
|
|
@@ -497,7 +519,9 @@ function ConversationNotice({ notice }: { notice: AgentConversationNotice }) {
|
|
|
497
519
|
<IconAlertTriangle size={15} />
|
|
498
520
|
<div>
|
|
499
521
|
{notice.title && <strong>{notice.title}</strong>}
|
|
500
|
-
|
|
522
|
+
{/* Tone doesn't mark failures: a harness error that mentions approval
|
|
523
|
+
is classified as an approval and rendered with the warning tone. */}
|
|
524
|
+
<span {...SESSION_REPLAY_MASK_PROPS}>{notice.text}</span>
|
|
501
525
|
</div>
|
|
502
526
|
{notice.action}
|
|
503
527
|
</div>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { appendAgentChatContextToMessage } from "@agent-native/core/shared";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
|
|
4
|
+
import { fallbackChatTitle } from "./fallback-chat-title.js";
|
|
5
|
+
|
|
6
|
+
describe("fallbackChatTitle", () => {
|
|
7
|
+
it("strips inline legacy context whether it is closed or unclosed", () => {
|
|
8
|
+
expect(fallbackChatTitle("Plan <context>private</context> next")).toBe(
|
|
9
|
+
"Plan next",
|
|
10
|
+
);
|
|
11
|
+
expect(fallbackChatTitle("Plan <context>private remainder")).toBe("Plan");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("does not let a nested encoded-looking marker escape legacy context", () => {
|
|
15
|
+
expect(
|
|
16
|
+
fallbackChatTitle(
|
|
17
|
+
'Plan <context>private <context data-agentkit-context-encoding="entities-v1">nested</context> private</context> next',
|
|
18
|
+
),
|
|
19
|
+
).toBe("Plan next");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("does not strip longer or dotted tag names", () => {
|
|
23
|
+
const prompt =
|
|
24
|
+
"<context-menu>ok</context-menu> <Context.Provider>ok</Context.Provider>";
|
|
25
|
+
|
|
26
|
+
expect(fallbackChatTitle(prompt)).toBe(prompt);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("preserves raw lookalike tags before a canonical encoded context block", () => {
|
|
30
|
+
const message =
|
|
31
|
+
"A <context-menu>x</context-menu> <Context.Provider>y</Context.Provider>";
|
|
32
|
+
const prompt = `${message}\n\n<context data-agentkit-context-encoding="entities-v1">\nprivate\n</context>`;
|
|
33
|
+
|
|
34
|
+
expect(fallbackChatTitle(prompt)).toBe(message);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("preserves an authored exact marker through the encoded producer", () => {
|
|
38
|
+
expect(
|
|
39
|
+
fallbackChatTitle(
|
|
40
|
+
appendAgentChatContextToMessage("<context>", "private </context>"),
|
|
41
|
+
),
|
|
42
|
+
).toBe("<context>");
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
+
import { stripAgentChatContextFromMessage } from "@agent-native/core/shared";
|
|
2
|
+
|
|
1
3
|
export function fallbackChatTitle(message: string): string {
|
|
2
|
-
return message
|
|
3
|
-
.replace(/<context\b[^>]*>[\s\S]*?<\/context>\n?/gi, "")
|
|
4
|
-
.replace(/<context\b[^>]*>[\s\S]*$/gi, "")
|
|
5
|
-
.replace(/<\/context>/gi, "")
|
|
4
|
+
return stripAgentChatContextFromMessage(message)
|
|
6
5
|
.replace(/@\[([^\]|]+)\|[^\]]*\]/g, "@$1")
|
|
7
6
|
.replace(/\s+/g, " ")
|
|
8
7
|
.trim()
|
|
@@ -78,6 +78,7 @@ vi.mock(
|
|
|
78
78
|
},
|
|
79
79
|
);
|
|
80
80
|
|
|
81
|
+
import { SESSION_REPLAY_BLOCK_ATTRIBUTE } from "@agent-native/core/client/session-replay-privacy";
|
|
81
82
|
import {
|
|
82
83
|
AGENT_NATIVE_EMBED_MESSAGE_TYPES,
|
|
83
84
|
AGENT_NATIVE_EMBED_PROTOCOL,
|
|
@@ -291,6 +292,29 @@ describe("McpAppRenderer security helpers", () => {
|
|
|
291
292
|
);
|
|
292
293
|
});
|
|
293
294
|
|
|
295
|
+
it("blocks the frame from replays once the app fails on its own", async () => {
|
|
296
|
+
vi.useFakeTimers();
|
|
297
|
+
const payload = mcpAppPayload({
|
|
298
|
+
resourceHtml: "<!doctype html><html><body>Jane Doe's notes</body></html>",
|
|
299
|
+
openUrl: "https://plan.agent-native.com/plans/plan-123",
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
await act(async () => {
|
|
303
|
+
root.render(React.createElement(McpAppRenderer, { app: payload }));
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
const iframe = container.querySelector("iframe");
|
|
307
|
+
expect(iframe?.hasAttribute(SESSION_REPLAY_BLOCK_ATTRIBUTE)).toBe(false);
|
|
308
|
+
|
|
309
|
+
await act(async () => {
|
|
310
|
+
vi.advanceTimersByTime(MCP_APP_INITIALIZE_TIMEOUT_MS + 1);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
expect(container.querySelector(".agent-mcp-app__error-box")).toBeTruthy();
|
|
314
|
+
expect(container.querySelector("iframe")).toBe(iframe);
|
|
315
|
+
expect(iframe?.hasAttribute(SESSION_REPLAY_BLOCK_ATTRIBUTE)).toBe(true);
|
|
316
|
+
});
|
|
317
|
+
|
|
294
318
|
it("initializes the v2 bridge and proxies MCP tool listing and calls through the app API", async () => {
|
|
295
319
|
appBridgeHarness.enabled = true;
|
|
296
320
|
const callTool = vi.fn(async () => ({
|
|
@@ -32,6 +32,11 @@ import {
|
|
|
32
32
|
useState,
|
|
33
33
|
} from "react";
|
|
34
34
|
|
|
35
|
+
import {
|
|
36
|
+
SESSION_REPLAY_BLOCK_PROPS,
|
|
37
|
+
SESSION_REPLAY_MASK_PROPS,
|
|
38
|
+
} from "../session-replay-privacy.js";
|
|
39
|
+
|
|
35
40
|
type CallToolResult = Parameters<
|
|
36
41
|
InstanceType<typeof AppBridge>["sendToolResult"]
|
|
37
42
|
>[0];
|
|
@@ -409,12 +414,13 @@ export function McpAppRenderer({
|
|
|
409
414
|
<div className="agent-mcp-app__error" role="alert">
|
|
410
415
|
<div className="agent-mcp-app__error-box">
|
|
411
416
|
<IconAlertTriangle size={15} />
|
|
412
|
-
<span>{error}</span>
|
|
417
|
+
<span {...SESSION_REPLAY_MASK_PROPS}>{error}</span>
|
|
413
418
|
</div>
|
|
414
419
|
</div>
|
|
415
420
|
)}
|
|
416
421
|
{srcDoc ? (
|
|
417
422
|
<iframe
|
|
423
|
+
{...(error ? SESSION_REPLAY_BLOCK_PROPS : {})}
|
|
418
424
|
ref={iframeRef}
|
|
419
425
|
title={app.tool?.title ?? app.originalToolName}
|
|
420
426
|
srcDoc={srcDoc}
|
|
@@ -454,7 +460,7 @@ export function McpAppRenderer({
|
|
|
454
460
|
<div className="agent-mcp-app__error" role="alert">
|
|
455
461
|
<div className="agent-mcp-app__error-box">
|
|
456
462
|
<IconAlertTriangle size={15} />
|
|
457
|
-
<span>{error}</span>
|
|
463
|
+
<span {...SESSION_REPLAY_MASK_PROPS}>{error}</span>
|
|
458
464
|
{externalOpenUrl && !readOnly && (
|
|
459
465
|
<button
|
|
460
466
|
type="button"
|
|
@@ -469,7 +475,9 @@ export function McpAppRenderer({
|
|
|
469
475
|
</div>
|
|
470
476
|
</div>
|
|
471
477
|
)}
|
|
478
|
+
{/* A failed app stays mounted, and the recorder keeps its frame attributes. */}
|
|
472
479
|
<iframe
|
|
480
|
+
{...(error ? SESSION_REPLAY_BLOCK_PROPS : {})}
|
|
473
481
|
ref={iframeRef}
|
|
474
482
|
title={app.tool?.title ?? app.originalToolName}
|
|
475
483
|
srcDoc={srcDoc}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as core from "@agent-native/core/client/session-replay-privacy";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
SESSION_REPLAY_BLOCK_PROPS,
|
|
6
|
+
SESSION_REPLAY_MASK_PROPS,
|
|
7
|
+
} from "./session-replay-privacy.js";
|
|
8
|
+
|
|
9
|
+
describe("toolkit session replay markers", () => {
|
|
10
|
+
it("match the markers core's recorder honors", () => {
|
|
11
|
+
expect(SESSION_REPLAY_MASK_PROPS).toEqual(core.SESSION_REPLAY_MASK_PROPS);
|
|
12
|
+
expect(SESSION_REPLAY_BLOCK_PROPS).toEqual(core.SESSION_REPLAY_BLOCK_PROPS);
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Toolkit's core peer range includes releases without
|
|
2
|
+
// @agent-native/core/client/session-replay-privacy, so it repeats core's replay
|
|
3
|
+
// markers instead of importing them. Its spec asserts core's attribute names.
|
|
4
|
+
export const SESSION_REPLAY_MASK_PROPS = { "data-an-mask": "" } as const;
|
|
5
|
+
export const SESSION_REPLAY_BLOCK_PROPS = { "data-an-block": "" } as const;
|
|
@@ -36,7 +36,14 @@ import {
|
|
|
36
36
|
IconX,
|
|
37
37
|
} from "@tabler/icons-react";
|
|
38
38
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
39
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
useState,
|
|
41
|
+
useEffect,
|
|
42
|
+
useRef,
|
|
43
|
+
useCallback,
|
|
44
|
+
useMemo,
|
|
45
|
+
type ReactNode,
|
|
46
|
+
} from "react";
|
|
40
47
|
import { Link, useLocation, useNavigate } from "react-router";
|
|
41
48
|
|
|
42
49
|
import { AgentToggleButton } from "../chat/AgentSidebar.js";
|
|
@@ -124,6 +131,8 @@ interface Extension {
|
|
|
124
131
|
|
|
125
132
|
export interface ExtensionViewerProps {
|
|
126
133
|
extensionId: string;
|
|
134
|
+
/** Trailing toolbar controls. Defaults to the agent toggle. */
|
|
135
|
+
headerActions?: ReactNode;
|
|
127
136
|
}
|
|
128
137
|
|
|
129
138
|
function readExtensionTitleSuffix(): string | null {
|
|
@@ -698,7 +707,10 @@ function ExtensionHistoryPopover({
|
|
|
698
707
|
);
|
|
699
708
|
}
|
|
700
709
|
|
|
701
|
-
export function ExtensionViewer({
|
|
710
|
+
export function ExtensionViewer({
|
|
711
|
+
extensionId,
|
|
712
|
+
headerActions = <AgentToggleButton />,
|
|
713
|
+
}: ExtensionViewerProps) {
|
|
702
714
|
const t = useT();
|
|
703
715
|
const location = useLocation();
|
|
704
716
|
const navigate = useNavigate();
|
|
@@ -1242,7 +1254,7 @@ export function ExtensionViewer({ extensionId }: ExtensionViewerProps) {
|
|
|
1242
1254
|
/>
|
|
1243
1255
|
</>
|
|
1244
1256
|
)}
|
|
1245
|
-
|
|
1257
|
+
{headerActions}
|
|
1246
1258
|
</div>
|
|
1247
1259
|
</div>
|
|
1248
1260
|
{isLocalExtension && (
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { agentNativePath } from "@agent-native/core/client/api-path";
|
|
2
2
|
import { getBrowserTabId } from "@agent-native/core/client/hooks";
|
|
3
|
-
import { useEffect } from "react";
|
|
3
|
+
import { useEffect, type ReactNode } from "react";
|
|
4
4
|
import { useParams } from "react-router";
|
|
5
5
|
|
|
6
6
|
import { incrementExtensionView } from "./extension-popularity.js";
|
|
7
7
|
import { ExtensionsListPage } from "./ExtensionsListPage.js";
|
|
8
8
|
import { ExtensionViewer } from "./ExtensionViewer.js";
|
|
9
9
|
|
|
10
|
-
export function ExtensionViewerPage(
|
|
10
|
+
export function ExtensionViewerPage({
|
|
11
|
+
headerActions,
|
|
12
|
+
}: {
|
|
13
|
+
headerActions?: ReactNode;
|
|
14
|
+
} = {}) {
|
|
11
15
|
const { id } = useParams<{ id: string }>();
|
|
12
16
|
|
|
13
17
|
useEffect(() => {
|
|
@@ -33,5 +37,5 @@ export function ExtensionViewerPage() {
|
|
|
33
37
|
return <ExtensionsListPage />;
|
|
34
38
|
}
|
|
35
39
|
if (!id) return null;
|
|
36
|
-
return <ExtensionViewer extensionId={id} />;
|
|
40
|
+
return <ExtensionViewer extensionId={id} headerActions={headerActions} />;
|
|
37
41
|
}
|