@agent-native/core 0.78.8 → 0.79.0
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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +25 -0
- package/corpus/core/docs/content/actions.md +4 -2
- package/corpus/core/docs/content/agent-surfaces.md +9 -0
- package/corpus/core/docs/content/extensions.md +10 -0
- package/corpus/core/docs/content/generative-ui.md +217 -0
- package/corpus/core/docs/content/key-concepts.md +2 -2
- package/corpus/core/docs/content/mcp-apps.md +3 -5
- package/corpus/core/docs/content/native-chat-ui.md +9 -5
- package/corpus/core/docs/content/tracking.md +9 -3
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/action-ui.ts +1 -0
- package/corpus/core/src/client/analytics.ts +195 -12
- package/corpus/core/src/client/chat/index.ts +1 -0
- package/corpus/core/src/client/chat/tool-call-display.tsx +5 -2
- package/corpus/core/src/client/chat/widgets/InlineExtensionWidget.tsx +139 -0
- package/corpus/core/src/client/chat/widgets/builtin-tool-renderers.tsx +27 -1
- package/corpus/core/src/client/conversation/AgentConversation.tsx +38 -0
- package/corpus/core/src/client/conversation/code-agent-transcript.ts +9 -0
- package/corpus/core/src/client/conversation/types.ts +4 -0
- package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +27 -5
- package/corpus/core/src/client/extensions/ExtensionViewer.tsx +26 -5
- package/corpus/core/src/client/extensions/InlineExtensionFrame.tsx +654 -0
- package/corpus/core/src/client/extensions/iframe-bridge.ts +30 -0
- package/corpus/core/src/client/extensions/index.ts +5 -0
- package/corpus/core/src/client/index.ts +1 -0
- package/corpus/core/src/client/session-replay.ts +89 -14
- package/corpus/core/src/code-agents/transcript-normalizer.ts +14 -0
- package/corpus/core/src/extensions/actions.ts +218 -1
- package/corpus/core/src/extensions/html-shell.ts +70 -3
- package/corpus/core/src/extensions/routes.ts +12 -4
- package/corpus/core/src/index.browser.ts +1 -0
- package/corpus/core/src/index.ts +1 -0
- package/corpus/core/src/mcp/build-server.ts +1 -1
- package/corpus/core/src/mcp/embed-app.ts +11 -1
- package/corpus/core/src/server/agent-chat-plugin.ts +21 -9
- package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +13 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/references/api.md +2 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +115 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +198 -59
- package/corpus/templates/analytics/AGENTS.md +12 -0
- package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +1105 -0
- package/corpus/templates/analytics/actions/dashboard-panel-order.ts +169 -0
- package/corpus/templates/analytics/actions/get-sql-dashboard.ts +89 -6
- package/corpus/templates/analytics/actions/mutate-dashboard.ts +333 -0
- package/corpus/templates/analytics/actions/reorder-dashboard-panels.ts +214 -0
- package/corpus/templates/analytics/actions/send-dashboard-report-now.ts +48 -21
- package/corpus/templates/analytics/actions/update-dashboard.ts +193 -100
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +11 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/analytics/app/global.css +81 -10
- package/corpus/templates/analytics/app/i18n-data.ts +249 -4
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/ChartCard.tsx +3 -3
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +41 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/EmailReportDialog.tsx +1 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +4 -4
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +48 -18
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +32 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +204 -70
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +1101 -227
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +160 -133
- package/corpus/templates/analytics/changelog/2026-06-26-agents-can-now-edit-dashboards-through-a-typed-mutation-scri.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-dashboard-bar-chart-hovers-stay-subtle-in-dark-mode.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-dashboard-chart-moves-are-now-handled-by-panel-id-making-sim.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-dashboard-charts-drag-smoothly-with-stable-cards-inactive-ch.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-dashboard-charts-show-a-refresh-spinner-while-keeping-existi.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-dashboard-edits-stay-stable-while-chart-order-and-deletions-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-dashboard-email-reports-queue-immediate-sends.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-explorer-dashboards-now-drag-charts-with-a-stable-preview-an.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-session-replay-playback-can-now-be-toggled-by-clicking-the-r.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-session-replays-now-open-from-the-full-row-and-play-with-tim.md +6 -0
- package/corpus/templates/analytics/server/lib/dashboard-report-subscriptions.ts +21 -0
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +41 -17
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +5 -0
- package/dist/action-ui.d.ts +1 -0
- package/dist/action-ui.d.ts.map +1 -1
- package/dist/action-ui.js +1 -0
- package/dist/action-ui.js.map +1 -1
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +173 -12
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/chat/index.d.ts +1 -1
- package/dist/client/chat/index.d.ts.map +1 -1
- package/dist/client/chat/index.js +1 -1
- package/dist/client/chat/index.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +3 -1
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/chat/widgets/InlineExtensionWidget.d.ts +17 -0
- package/dist/client/chat/widgets/InlineExtensionWidget.d.ts.map +1 -0
- package/dist/client/chat/widgets/InlineExtensionWidget.js +85 -0
- package/dist/client/chat/widgets/InlineExtensionWidget.js.map +1 -0
- package/dist/client/chat/widgets/builtin-tool-renderers.d.ts.map +1 -1
- package/dist/client/chat/widgets/builtin-tool-renderers.js +15 -2
- package/dist/client/chat/widgets/builtin-tool-renderers.js.map +1 -1
- package/dist/client/conversation/AgentConversation.d.ts.map +1 -1
- package/dist/client/conversation/AgentConversation.js +33 -0
- package/dist/client/conversation/AgentConversation.js.map +1 -1
- package/dist/client/conversation/code-agent-transcript.js +8 -0
- package/dist/client/conversation/code-agent-transcript.js.map +1 -1
- package/dist/client/conversation/types.d.ts +4 -0
- package/dist/client/conversation/types.d.ts.map +1 -1
- package/dist/client/conversation/types.js.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.js +29 -1
- package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
- package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionViewer.js +28 -1
- package/dist/client/extensions/ExtensionViewer.js.map +1 -1
- package/dist/client/extensions/InlineExtensionFrame.d.ts +25 -0
- package/dist/client/extensions/InlineExtensionFrame.d.ts.map +1 -0
- package/dist/client/extensions/InlineExtensionFrame.js +488 -0
- package/dist/client/extensions/InlineExtensionFrame.js.map +1 -0
- package/dist/client/extensions/iframe-bridge.d.ts +2 -0
- package/dist/client/extensions/iframe-bridge.d.ts.map +1 -1
- package/dist/client/extensions/iframe-bridge.js +23 -0
- package/dist/client/extensions/iframe-bridge.js.map +1 -1
- package/dist/client/extensions/index.d.ts +1 -0
- package/dist/client/extensions/index.d.ts.map +1 -1
- package/dist/client/extensions/index.js +1 -0
- package/dist/client/extensions/index.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/session-replay.d.ts +11 -1
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +74 -14
- package/dist/client/session-replay.js.map +1 -1
- package/dist/code-agents/transcript-normalizer.d.ts +2 -0
- package/dist/code-agents/transcript-normalizer.d.ts.map +1 -1
- package/dist/code-agents/transcript-normalizer.js +8 -0
- package/dist/code-agents/transcript-normalizer.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +2 -2
- package/dist/extensions/actions.d.ts.map +1 -1
- package/dist/extensions/actions.js +200 -1
- package/dist/extensions/actions.js.map +1 -1
- package/dist/extensions/html-shell.d.ts.map +1 -1
- package/dist/extensions/html-shell.js +70 -3
- package/dist/extensions/html-shell.js.map +1 -1
- package/dist/extensions/routes.d.ts.map +1 -1
- package/dist/extensions/routes.js +11 -4
- package/dist/extensions/routes.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/build-server.js +1 -1
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/embed-app.d.ts.map +1 -1
- package/dist/mcp/embed-app.js +11 -1
- package/dist/mcp/embed-app.js.map +1 -1
- package/dist/observability/routes.d.ts +7 -7
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +21 -9
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +2 -2
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +13 -0
- package/dist/templates/workspace-core/.agents/skills/extensions/references/api.md +2 -0
- package/dist/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +115 -0
- package/docs/content/actions.md +4 -2
- package/docs/content/agent-surfaces.md +9 -0
- package/docs/content/extensions.md +10 -0
- package/docs/content/generative-ui.md +217 -0
- package/docs/content/key-concepts.md +2 -2
- package/docs/content/mcp-apps.md +3 -5
- package/docs/content/native-chat-ui.md +9 -5
- package/docs/content/tracking.md +9 -3
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +13 -0
- package/src/templates/workspace-core/.agents/skills/extensions/references/api.md +2 -0
- package/src/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +115 -0
|
@@ -59,6 +59,13 @@ type SentryUser = {
|
|
|
59
59
|
username?: string;
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
+
type TrackingIdentity = {
|
|
63
|
+
userId?: string;
|
|
64
|
+
userEmail?: string;
|
|
65
|
+
userName?: string;
|
|
66
|
+
orgId?: string | null;
|
|
67
|
+
};
|
|
68
|
+
|
|
62
69
|
let _getDefaultProps: GetDefaultProps | null = null;
|
|
63
70
|
let _agentNativeAnalyticsPublicKey: string | null = null;
|
|
64
71
|
let _agentNativeAnalyticsEndpoint: string | null = null;
|
|
@@ -67,6 +74,10 @@ let _sentryInitialized = false;
|
|
|
67
74
|
let _llmConnectionStatus: LlmConnectionStatus | null = null;
|
|
68
75
|
let _llmConnectionRefresh: Promise<void> | null = null;
|
|
69
76
|
let _llmConnectionRefreshInstalled = false;
|
|
77
|
+
let _trackingIdentity: TrackingIdentity | null = null;
|
|
78
|
+
let _trackingIdentityResolved = false;
|
|
79
|
+
let _trackingSessionRefresh: Promise<void> | null = null;
|
|
80
|
+
let _trackingSessionRefreshInstalled = false;
|
|
70
81
|
// Buffer for setSentryUser calls made before Sentry has initialized.
|
|
71
82
|
// `undefined` means "no pending update"; `null` means "pending clear".
|
|
72
83
|
let _pendingSentryUser: SentryUser | null | undefined = undefined;
|
|
@@ -249,6 +260,83 @@ function installLlmConnectionRefresh(): void {
|
|
|
249
260
|
});
|
|
250
261
|
}
|
|
251
262
|
|
|
263
|
+
function readTrackingString(value: unknown): string | undefined {
|
|
264
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function setTrackingIdentityFromSession(data: unknown): void {
|
|
268
|
+
const session = data as Record<string, unknown> | null;
|
|
269
|
+
if (!session || typeof session !== "object" || session.error) {
|
|
270
|
+
_trackingIdentity = null;
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const email = readTrackingString(session.email);
|
|
274
|
+
const authUserId = readTrackingString(session.userId);
|
|
275
|
+
const userId = email || authUserId;
|
|
276
|
+
if (!userId) {
|
|
277
|
+
_trackingIdentity = null;
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
const userName = readTrackingString(session.name);
|
|
281
|
+
_trackingIdentity = {
|
|
282
|
+
userId,
|
|
283
|
+
...(email ? { userEmail: email } : {}),
|
|
284
|
+
...(userName ? { userName } : {}),
|
|
285
|
+
orgId: readTrackingString(session.orgId) ?? null,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function refreshTrackingAuthSession(): Promise<void> {
|
|
290
|
+
if (typeof window === "undefined" || typeof fetch !== "function") {
|
|
291
|
+
_trackingIdentityResolved = true;
|
|
292
|
+
return Promise.resolve();
|
|
293
|
+
}
|
|
294
|
+
if (_trackingSessionRefresh) return _trackingSessionRefresh;
|
|
295
|
+
_trackingSessionRefresh = fetch(
|
|
296
|
+
agentNativePath("/_agent-native/auth/session"),
|
|
297
|
+
)
|
|
298
|
+
.then((res) => (res.ok ? res.json() : null))
|
|
299
|
+
.then((data) => {
|
|
300
|
+
setTrackingIdentityFromSession(data);
|
|
301
|
+
})
|
|
302
|
+
.catch(() => {
|
|
303
|
+
_trackingIdentity = null;
|
|
304
|
+
})
|
|
305
|
+
.finally(() => {
|
|
306
|
+
_trackingIdentityResolved = true;
|
|
307
|
+
_trackingSessionRefresh = null;
|
|
308
|
+
});
|
|
309
|
+
return _trackingSessionRefresh;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function installTrackingAuthSessionRefresh(): void {
|
|
313
|
+
if (typeof window === "undefined" || _trackingSessionRefreshInstalled) return;
|
|
314
|
+
_trackingSessionRefreshInstalled = true;
|
|
315
|
+
void refreshTrackingAuthSession();
|
|
316
|
+
window.addEventListener("focus", () => {
|
|
317
|
+
void refreshTrackingAuthSession();
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function applyTrackingIdentity(
|
|
322
|
+
properties: Record<string, unknown>,
|
|
323
|
+
): Record<string, unknown> {
|
|
324
|
+
const identity = _trackingIdentity;
|
|
325
|
+
if (!identity) return properties;
|
|
326
|
+
let next = properties;
|
|
327
|
+
const assign = (key: string, value: unknown) => {
|
|
328
|
+
if (value !== undefined && value !== null && next[key] === undefined) {
|
|
329
|
+
if (next === properties) next = { ...properties };
|
|
330
|
+
next[key] = value;
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
assign("userId", identity.userId);
|
|
334
|
+
assign("userEmail", identity.userEmail);
|
|
335
|
+
assign("userName", identity.userName);
|
|
336
|
+
assign("orgId", identity.orgId);
|
|
337
|
+
return next;
|
|
338
|
+
}
|
|
339
|
+
|
|
252
340
|
function getOrCreateAnonymousId(): string | undefined {
|
|
253
341
|
if (typeof window === "undefined") return undefined;
|
|
254
342
|
let id = safeStorageGet(ANONYMOUS_ID_STORAGE_KEY);
|
|
@@ -665,6 +753,20 @@ export function setSentryUser(
|
|
|
665
753
|
user: SentryUser | null,
|
|
666
754
|
orgId?: string | null,
|
|
667
755
|
): void {
|
|
756
|
+
if (user) {
|
|
757
|
+
const userId = user.email || user.id;
|
|
758
|
+
_trackingIdentity = userId
|
|
759
|
+
? {
|
|
760
|
+
userId,
|
|
761
|
+
...(user.email ? { userEmail: user.email } : {}),
|
|
762
|
+
...(user.username ? { userName: user.username } : {}),
|
|
763
|
+
orgId: orgId ?? null,
|
|
764
|
+
}
|
|
765
|
+
: null;
|
|
766
|
+
} else {
|
|
767
|
+
_trackingIdentity = null;
|
|
768
|
+
}
|
|
769
|
+
_trackingIdentityResolved = true;
|
|
668
770
|
if (_sentryInitialized) {
|
|
669
771
|
Sentry.setUser(user);
|
|
670
772
|
if (orgId !== undefined) {
|
|
@@ -775,6 +877,7 @@ export function configureTracking(options: ConfigureTrackingOptions): void {
|
|
|
775
877
|
ensureAmplitude();
|
|
776
878
|
captureFirstTouchAttribution();
|
|
777
879
|
installLlmConnectionRefresh();
|
|
880
|
+
installTrackingAuthSessionRefresh();
|
|
778
881
|
installPageviewTracking();
|
|
779
882
|
maybeInstallSessionReplay(options.sessionReplay, {
|
|
780
883
|
endpoint: options.endpoint,
|
|
@@ -791,6 +894,14 @@ function sessionReplayEnabledFromEnv(): boolean {
|
|
|
791
894
|
return /^(1|true|yes|on)$/i.test((value ?? "").trim());
|
|
792
895
|
}
|
|
793
896
|
|
|
897
|
+
function sessionReplayRequiresSignedInUserFromEnv(): boolean {
|
|
898
|
+
const env = (import.meta.env as Record<string, string | undefined>) ?? {};
|
|
899
|
+
const value =
|
|
900
|
+
env.VITE_AGENT_NATIVE_SESSION_REPLAY_REQUIRE_AUTH ||
|
|
901
|
+
env.VITE_SESSION_REPLAY_REQUIRE_AUTH;
|
|
902
|
+
return /^(1|true|yes|on)$/i.test((value ?? "").trim());
|
|
903
|
+
}
|
|
904
|
+
|
|
794
905
|
function configuredSessionReplayOptions(
|
|
795
906
|
config: boolean | SessionReplayOptions | undefined,
|
|
796
907
|
tracking: { endpoint?: string; publicKey?: string } = {},
|
|
@@ -802,12 +913,22 @@ function configuredSessionReplayOptions(
|
|
|
802
913
|
: undefined;
|
|
803
914
|
const withTrackingDefaults = (
|
|
804
915
|
options: SessionReplayOptions,
|
|
805
|
-
): SessionReplayOptions =>
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
916
|
+
): SessionReplayOptions => {
|
|
917
|
+
const extraProperties = replayExtraPropertiesWithDefaults(
|
|
918
|
+
options.extraProperties,
|
|
919
|
+
);
|
|
920
|
+
return {
|
|
921
|
+
...(publicKey && !options.publicKey ? { publicKey } : {}),
|
|
922
|
+
...(endpoint && !options.endpoint ? { endpoint } : {}),
|
|
923
|
+
...options,
|
|
924
|
+
requireSignedInUser:
|
|
925
|
+
options.requireSignedInUser ??
|
|
926
|
+
sessionReplayRequiresSignedInUserFromEnv(),
|
|
927
|
+
...(extraProperties ? { extraProperties } : {}),
|
|
928
|
+
};
|
|
929
|
+
};
|
|
810
930
|
|
|
931
|
+
if (config === false) return null;
|
|
811
932
|
if (config === true) return withTrackingDefaults({});
|
|
812
933
|
if (config && typeof config === "object") {
|
|
813
934
|
if (config.enabled === false) return null;
|
|
@@ -816,6 +937,22 @@ function configuredSessionReplayOptions(
|
|
|
816
937
|
return sessionReplayEnabledFromEnv() ? withTrackingDefaults({}) : null;
|
|
817
938
|
}
|
|
818
939
|
|
|
940
|
+
function replayExtraPropertiesWithDefaults(
|
|
941
|
+
source: SessionReplayOptions["extraProperties"],
|
|
942
|
+
): SessionReplayOptions["extraProperties"] {
|
|
943
|
+
return () => {
|
|
944
|
+
const rawProps =
|
|
945
|
+
typeof source === "function"
|
|
946
|
+
? source()
|
|
947
|
+
: source && typeof source === "object"
|
|
948
|
+
? source
|
|
949
|
+
: {};
|
|
950
|
+
const props = rawProps && typeof rawProps === "object" ? rawProps : {};
|
|
951
|
+
const withDefaults = _getDefaultProps?.("session_replay", props) ?? props;
|
|
952
|
+
return applyTrackingIdentity(withDefaults);
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
|
|
819
956
|
function replayEndpointFromTrackingEndpoint(value: string): string | undefined {
|
|
820
957
|
try {
|
|
821
958
|
const url = new URL(value);
|
|
@@ -849,23 +986,60 @@ function maybeInstallSessionReplay(
|
|
|
849
986
|
if (typeof window === "undefined") return;
|
|
850
987
|
const options = configuredSessionReplayOptions(config, tracking);
|
|
851
988
|
if (!options) return;
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
989
|
+
void startConfiguredSessionReplay(options);
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
async function waitForSessionReplayAuthIfRequired(
|
|
993
|
+
options: SessionReplayOptions,
|
|
994
|
+
): Promise<boolean> {
|
|
995
|
+
if (!options.requireSignedInUser) return true;
|
|
996
|
+
if (_trackingIdentity?.userId) return true;
|
|
997
|
+
try {
|
|
998
|
+
if (_trackingSessionRefresh) {
|
|
999
|
+
await _trackingSessionRefresh;
|
|
1000
|
+
} else if (!_trackingIdentityResolved) {
|
|
1001
|
+
await refreshTrackingAuthSession();
|
|
1002
|
+
}
|
|
1003
|
+
} catch {
|
|
1004
|
+
// best-effort; missing identity below keeps replay off
|
|
1005
|
+
}
|
|
1006
|
+
return !!_trackingIdentity?.userId;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
async function startConfiguredSessionReplay(
|
|
1010
|
+
options: SessionReplayOptions,
|
|
1011
|
+
): Promise<SessionReplayStartResult | null> {
|
|
1012
|
+
if (!(await waitForSessionReplayAuthIfRequired(options))) {
|
|
1013
|
+
return { started: false, reason: "missing-user-id" };
|
|
1014
|
+
}
|
|
1015
|
+
try {
|
|
1016
|
+
const mod = await import("./session-replay.js");
|
|
1017
|
+
return mod.startSessionReplay(options);
|
|
1018
|
+
} catch {
|
|
1019
|
+
return { started: false, reason: "import-failed" };
|
|
1020
|
+
}
|
|
855
1021
|
}
|
|
856
1022
|
|
|
857
1023
|
export async function startSessionReplay(
|
|
858
1024
|
options: SessionReplayOptions = {},
|
|
859
1025
|
): Promise<SessionReplayStartResult> {
|
|
1026
|
+
const configured = configuredSessionReplayOptions(options) ?? options;
|
|
1027
|
+
if (!(await waitForSessionReplayAuthIfRequired(configured))) {
|
|
1028
|
+
return { started: false, reason: "missing-user-id" };
|
|
1029
|
+
}
|
|
860
1030
|
const mod = await import("./session-replay.js");
|
|
861
|
-
return mod.startSessionReplay(
|
|
1031
|
+
return mod.startSessionReplay(configured);
|
|
862
1032
|
}
|
|
863
1033
|
|
|
864
1034
|
export async function maybeStartSessionReplay(
|
|
865
1035
|
options: SessionReplayOptions = {},
|
|
866
1036
|
): Promise<SessionReplayStartResult> {
|
|
1037
|
+
const configured = configuredSessionReplayOptions(options) ?? options;
|
|
1038
|
+
if (!(await waitForSessionReplayAuthIfRequired(configured))) {
|
|
1039
|
+
return { started: false, reason: "missing-user-id" };
|
|
1040
|
+
}
|
|
867
1041
|
const mod = await import("./session-replay.js");
|
|
868
|
-
return mod.maybeStartSessionReplay(
|
|
1042
|
+
return mod.maybeStartSessionReplay(configured);
|
|
869
1043
|
}
|
|
870
1044
|
|
|
871
1045
|
export async function stopSessionReplay(reason = "manual"): Promise<void> {
|
|
@@ -911,7 +1085,7 @@ function resolveProps(
|
|
|
911
1085
|
for (const [key, value] of Object.entries(llmProps)) {
|
|
912
1086
|
if (enriched[key] === undefined) enriched[key] = value;
|
|
913
1087
|
}
|
|
914
|
-
return enriched;
|
|
1088
|
+
return applyTrackingIdentity(enriched);
|
|
915
1089
|
}
|
|
916
1090
|
|
|
917
1091
|
function pageviewKey(): string {
|
|
@@ -950,11 +1124,20 @@ function emitPageview(reason: string): void {
|
|
|
950
1124
|
|
|
951
1125
|
function schedulePageview(reason: string): void {
|
|
952
1126
|
const run = () => emitPageview(reason);
|
|
1127
|
+
const pendingStartupContext: Array<Promise<void>> = [];
|
|
953
1128
|
if (_llmConnectionRefresh && !_llmConnectionStatus) {
|
|
1129
|
+
pendingStartupContext.push(_llmConnectionRefresh);
|
|
1130
|
+
}
|
|
1131
|
+
if (_trackingSessionRefresh && !_trackingIdentityResolved) {
|
|
1132
|
+
pendingStartupContext.push(_trackingSessionRefresh);
|
|
1133
|
+
}
|
|
1134
|
+
if (pendingStartupContext.length > 0) {
|
|
954
1135
|
const timeout = new Promise<void>((resolve) =>
|
|
955
1136
|
window.setTimeout(resolve, 250),
|
|
956
1137
|
);
|
|
957
|
-
Promise.race([
|
|
1138
|
+
Promise.race([Promise.allSettled(pendingStartupContext), timeout]).finally(
|
|
1139
|
+
run,
|
|
1140
|
+
);
|
|
958
1141
|
return;
|
|
959
1142
|
}
|
|
960
1143
|
if (typeof queueMicrotask === "function") {
|
|
@@ -727,19 +727,22 @@ export function ReconnectStreamMessage({
|
|
|
727
727
|
content: ContentPart[];
|
|
728
728
|
}) {
|
|
729
729
|
const chatRunning = React.useContext(ChatRunningContext);
|
|
730
|
+
const streamingTextPartIndex =
|
|
731
|
+
content.at(-1)?.type === "text" ? content.length - 1 : -1;
|
|
730
732
|
|
|
731
733
|
return (
|
|
732
734
|
<div className="flex justify-start">
|
|
733
735
|
<div className="max-w-[95%] text-sm leading-relaxed text-foreground space-y-1">
|
|
734
736
|
{content.map((part, i) => {
|
|
735
737
|
if (part.type === "text") {
|
|
738
|
+
const partStreaming = chatRunning && i === streamingTextPartIndex;
|
|
736
739
|
return (
|
|
737
740
|
<SmoothMarkdownText
|
|
738
741
|
key={`reconnect-text-${i}`}
|
|
739
742
|
text={part.text}
|
|
740
|
-
streaming={
|
|
743
|
+
streaming={partStreaming}
|
|
741
744
|
resetKey={`reconnect-text-${i}`}
|
|
742
|
-
statusType={
|
|
745
|
+
statusType={partStreaming ? "running" : "complete"}
|
|
743
746
|
/>
|
|
744
747
|
);
|
|
745
748
|
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { IconExternalLink, IconSparkles } from "@tabler/icons-react";
|
|
2
|
+
|
|
3
|
+
import { extensionPath } from "../../../extensions/path.js";
|
|
4
|
+
import { appPath } from "../../api-path.js";
|
|
5
|
+
import { InlineExtensionFrame } from "../../extensions/InlineExtensionFrame.js";
|
|
6
|
+
import type { ToolRendererContext } from "../tool-render-registry.js";
|
|
7
|
+
|
|
8
|
+
export interface InlineExtensionToolResult {
|
|
9
|
+
mode: "transient" | "persisted";
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
content?: string;
|
|
14
|
+
path?: string;
|
|
15
|
+
updatedAt?: string;
|
|
16
|
+
context?: Record<string, unknown>;
|
|
17
|
+
initialHeight?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
21
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function stringValue(value: unknown): string | undefined {
|
|
25
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function recordValue(value: unknown): Record<string, unknown> | undefined {
|
|
29
|
+
return isRecord(value) ? value : undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function initialHeightValue(value: unknown): number | undefined {
|
|
33
|
+
const height = Number(value);
|
|
34
|
+
if (!Number.isFinite(height) || height <= 0) return undefined;
|
|
35
|
+
return Math.min(Math.max(Math.round(height), 120), 1000);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function normalizeInlineExtension(
|
|
39
|
+
value: unknown,
|
|
40
|
+
): InlineExtensionToolResult | null {
|
|
41
|
+
if (!isRecord(value)) return null;
|
|
42
|
+
const id = stringValue(value.id);
|
|
43
|
+
const name = stringValue(value.name);
|
|
44
|
+
if (!id || !name) return null;
|
|
45
|
+
const mode = value.mode === "transient" ? "transient" : "persisted";
|
|
46
|
+
const content = stringValue(value.content);
|
|
47
|
+
if (mode === "transient" && !content) return null;
|
|
48
|
+
return {
|
|
49
|
+
mode,
|
|
50
|
+
id,
|
|
51
|
+
name,
|
|
52
|
+
description: stringValue(value.description),
|
|
53
|
+
content,
|
|
54
|
+
path: stringValue(value.path),
|
|
55
|
+
updatedAt: stringValue(value.updatedAt),
|
|
56
|
+
context: recordValue(value.context),
|
|
57
|
+
initialHeight: initialHeightValue(value.initialHeight),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function normalizeInlineExtensionToolResult(
|
|
62
|
+
context: ToolRendererContext,
|
|
63
|
+
): InlineExtensionToolResult | null {
|
|
64
|
+
const result = context.resultJson;
|
|
65
|
+
if (!isRecord(result)) return null;
|
|
66
|
+
|
|
67
|
+
const inline = normalizeInlineExtension(result.inlineExtension);
|
|
68
|
+
if (inline) return inline;
|
|
69
|
+
|
|
70
|
+
if (isRecord(result.extension)) {
|
|
71
|
+
const id = stringValue(result.extension.id);
|
|
72
|
+
const name = stringValue(result.extension.name);
|
|
73
|
+
if (!id || !name) return null;
|
|
74
|
+
return {
|
|
75
|
+
mode: "persisted",
|
|
76
|
+
id,
|
|
77
|
+
name,
|
|
78
|
+
description: stringValue(result.extension.description),
|
|
79
|
+
path:
|
|
80
|
+
stringValue(result.extension.path) ??
|
|
81
|
+
stringValue(result.path) ??
|
|
82
|
+
extensionPath(id, name),
|
|
83
|
+
updatedAt: stringValue(result.extension.updatedAt),
|
|
84
|
+
context: recordValue(result.context),
|
|
85
|
+
initialHeight: initialHeightValue(result.initialHeight),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function InlineExtensionWidget({
|
|
93
|
+
context,
|
|
94
|
+
}: {
|
|
95
|
+
context: ToolRendererContext;
|
|
96
|
+
}) {
|
|
97
|
+
const result = normalizeInlineExtensionToolResult(context);
|
|
98
|
+
if (!result) return null;
|
|
99
|
+
|
|
100
|
+
const href = result.path ? appPath(result.path) : undefined;
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<div className="my-1.5 overflow-hidden rounded-lg border border-border bg-background text-foreground shadow-sm">
|
|
104
|
+
<div className="flex items-center gap-2 border-b border-border px-3 py-2">
|
|
105
|
+
<IconSparkles className="h-4 w-4 shrink-0 text-muted-foreground" />
|
|
106
|
+
<div className="min-w-0 flex-1">
|
|
107
|
+
<div className="truncate text-sm font-medium">{result.name}</div>
|
|
108
|
+
{result.description ? (
|
|
109
|
+
<div className="truncate text-[11px] text-muted-foreground">
|
|
110
|
+
{result.description}
|
|
111
|
+
</div>
|
|
112
|
+
) : null}
|
|
113
|
+
</div>
|
|
114
|
+
{href ? (
|
|
115
|
+
<a
|
|
116
|
+
href={href}
|
|
117
|
+
className="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-accent-foreground"
|
|
118
|
+
aria-label="Open extension"
|
|
119
|
+
>
|
|
120
|
+
<IconExternalLink className="h-4 w-4" />
|
|
121
|
+
</a>
|
|
122
|
+
) : null}
|
|
123
|
+
</div>
|
|
124
|
+
<InlineExtensionFrame
|
|
125
|
+
extensionId={result.mode === "persisted" ? result.id : undefined}
|
|
126
|
+
extension={{
|
|
127
|
+
id: result.id,
|
|
128
|
+
name: result.name,
|
|
129
|
+
description: result.description,
|
|
130
|
+
content: result.mode === "transient" ? result.content : undefined,
|
|
131
|
+
updatedAt: result.updatedAt,
|
|
132
|
+
mode: result.mode,
|
|
133
|
+
}}
|
|
134
|
+
context={result.context}
|
|
135
|
+
initialHeight={result.initialHeight ?? 260}
|
|
136
|
+
/>
|
|
137
|
+
</div>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
ACTION_CHAT_UI_DATA_INSIGHTS_RENDERER,
|
|
4
4
|
ACTION_CHAT_UI_DATA_TABLE_RENDERER,
|
|
5
5
|
ACTION_CHAT_UI_DATA_WIDGET_RENDERER,
|
|
6
|
+
ACTION_CHAT_UI_INLINE_EXTENSION_RENDERER,
|
|
6
7
|
} from "../../../action-ui.js";
|
|
7
8
|
import {
|
|
8
9
|
registerReservedActionChatRenderer,
|
|
@@ -21,6 +22,10 @@ import {
|
|
|
21
22
|
import { DataChartWidget } from "./DataChartWidget.js";
|
|
22
23
|
import { DataInsightsWidget } from "./DataInsightsWidget.js";
|
|
23
24
|
import { DataTableWidget } from "./DataTableWidget.js";
|
|
25
|
+
import {
|
|
26
|
+
InlineExtensionWidget,
|
|
27
|
+
normalizeInlineExtensionToolResult,
|
|
28
|
+
} from "./InlineExtensionWidget.js";
|
|
24
29
|
|
|
25
30
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
26
31
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
@@ -101,6 +106,11 @@ function renderDataWidget(context: ToolRendererContext) {
|
|
|
101
106
|
const BuiltinDataWidgetRenderer: ToolRendererComponent = ({ context }) =>
|
|
102
107
|
renderDataWidget(context);
|
|
103
108
|
|
|
109
|
+
const BuiltinInlineExtensionRenderer: ToolRendererComponent = ({ context }) =>
|
|
110
|
+
normalizeInlineExtensionToolResult(context) ? (
|
|
111
|
+
<InlineExtensionWidget context={context} />
|
|
112
|
+
) : null;
|
|
113
|
+
|
|
104
114
|
export function isBuiltinDataWidgetActionRenderer(
|
|
105
115
|
context: ToolRendererContext,
|
|
106
116
|
): boolean {
|
|
@@ -116,6 +126,12 @@ export function isBuiltinDataWidgetActionRenderer(
|
|
|
116
126
|
export function resolveBuiltinActionChatRenderer(
|
|
117
127
|
context: ToolRendererContext,
|
|
118
128
|
): ToolRendererComponent | null {
|
|
129
|
+
if (
|
|
130
|
+
context.chatUI?.renderer === ACTION_CHAT_UI_INLINE_EXTENSION_RENDERER &&
|
|
131
|
+
normalizeInlineExtensionToolResult(context)
|
|
132
|
+
) {
|
|
133
|
+
return BuiltinInlineExtensionRenderer;
|
|
134
|
+
}
|
|
119
135
|
if (
|
|
120
136
|
isBuiltinDataWidgetActionRenderer(context) &&
|
|
121
137
|
normalizeActionDataWidgetResult(context)
|
|
@@ -128,6 +144,12 @@ export function resolveBuiltinActionChatRenderer(
|
|
|
128
144
|
export function resolveBuiltinFallbackToolRenderer(
|
|
129
145
|
context: ToolRendererContext,
|
|
130
146
|
): ToolRendererComponent | null {
|
|
147
|
+
if (
|
|
148
|
+
context.chatUI?.renderer === ACTION_CHAT_UI_INLINE_EXTENSION_RENDERER &&
|
|
149
|
+
normalizeInlineExtensionToolResult(context)
|
|
150
|
+
) {
|
|
151
|
+
return BuiltinInlineExtensionRenderer;
|
|
152
|
+
}
|
|
131
153
|
return normalizeActionDataWidgetResult(context) !== null
|
|
132
154
|
? BuiltinDataWidgetRenderer
|
|
133
155
|
: null;
|
|
@@ -138,11 +160,15 @@ for (const [id, renderer] of [
|
|
|
138
160
|
["core.data-chart", ACTION_CHAT_UI_DATA_CHART_RENDERER],
|
|
139
161
|
["core.data-insights", ACTION_CHAT_UI_DATA_INSIGHTS_RENDERER],
|
|
140
162
|
["core.data-widget", ACTION_CHAT_UI_DATA_WIDGET_RENDERER],
|
|
163
|
+
["core.inline-extension", ACTION_CHAT_UI_INLINE_EXTENSION_RENDERER],
|
|
141
164
|
] as const) {
|
|
142
165
|
registerReservedActionChatRenderer({
|
|
143
166
|
id,
|
|
144
167
|
renderer,
|
|
145
|
-
Component:
|
|
168
|
+
Component:
|
|
169
|
+
renderer === ACTION_CHAT_UI_INLINE_EXTENSION_RENDERER
|
|
170
|
+
? BuiltinInlineExtensionRenderer
|
|
171
|
+
: BuiltinDataWidgetRenderer,
|
|
146
172
|
});
|
|
147
173
|
}
|
|
148
174
|
|
|
@@ -13,6 +13,11 @@ import React from "react";
|
|
|
13
13
|
import ReactMarkdown, { defaultUrlTransform } from "react-markdown";
|
|
14
14
|
import remarkGfm from "remark-gfm";
|
|
15
15
|
|
|
16
|
+
import { resolveToolRenderer } from "../chat/tool-render-registry.js";
|
|
17
|
+
import {
|
|
18
|
+
resolveBuiltinActionChatRenderer,
|
|
19
|
+
resolveBuiltinFallbackToolRenderer,
|
|
20
|
+
} from "../chat/widgets/builtin-tool-renderers.js";
|
|
16
21
|
import { HighlightedCodeBlock as SharedHighlightedCodeBlock } from "../HighlightedCodeBlock.js";
|
|
17
22
|
import { McpAppRenderer } from "../mcp-apps/McpAppRenderer.js";
|
|
18
23
|
import { humanizeToolName } from "../tool-display.js";
|
|
@@ -356,7 +361,40 @@ function openMarkdownLink(
|
|
|
356
361
|
window.open(url.href, "_blank", "noopener,noreferrer");
|
|
357
362
|
}
|
|
358
363
|
|
|
364
|
+
function parseJsonText(value: string | undefined): unknown {
|
|
365
|
+
if (!value) return null;
|
|
366
|
+
try {
|
|
367
|
+
return JSON.parse(value);
|
|
368
|
+
} catch {
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function parseJsonObject(value: string | undefined): Record<string, unknown> {
|
|
374
|
+
const parsed = parseJsonText(value);
|
|
375
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
376
|
+
? (parsed as Record<string, unknown>)
|
|
377
|
+
: {};
|
|
378
|
+
}
|
|
379
|
+
|
|
359
380
|
function ConversationToolCall({ tool }: { tool: AgentConversationToolCall }) {
|
|
381
|
+
const resultJson = parseJsonText(tool.result);
|
|
382
|
+
const nativeToolContext = {
|
|
383
|
+
toolName: tool.name,
|
|
384
|
+
args: tool.args ?? parseJsonObject(tool.input),
|
|
385
|
+
resultText: tool.result,
|
|
386
|
+
resultJson: tool.resultJson ?? resultJson,
|
|
387
|
+
isRunning: tool.state === "running" || tool.state === "activity",
|
|
388
|
+
chatUI: tool.chatUI,
|
|
389
|
+
};
|
|
390
|
+
const NativeToolRenderer =
|
|
391
|
+
resolveBuiltinActionChatRenderer(nativeToolContext) ??
|
|
392
|
+
resolveToolRenderer(nativeToolContext) ??
|
|
393
|
+
resolveBuiltinFallbackToolRenderer(nativeToolContext);
|
|
394
|
+
if (NativeToolRenderer) {
|
|
395
|
+
return <NativeToolRenderer context={nativeToolContext} />;
|
|
396
|
+
}
|
|
397
|
+
|
|
360
398
|
const hasDetails = Boolean(tool.input || tool.result || tool.mcpApp);
|
|
361
399
|
const icon =
|
|
362
400
|
tool.state === "running" || tool.state === "activity" ? (
|
|
@@ -175,9 +175,12 @@ function toConversationTool(
|
|
|
175
175
|
: item.state === "activity"
|
|
176
176
|
? "activity"
|
|
177
177
|
: "running",
|
|
178
|
+
args: recordValue(item.input),
|
|
178
179
|
input: preview(item.input),
|
|
180
|
+
resultJson: item.result,
|
|
179
181
|
result: preview(item.result),
|
|
180
182
|
...(item.mcpApp ? { mcpApp: item.mcpApp } : {}),
|
|
183
|
+
...(item.chatUI ? { chatUI: item.chatUI } : {}),
|
|
181
184
|
summary:
|
|
182
185
|
item.state === "completed"
|
|
183
186
|
? "finished"
|
|
@@ -229,6 +232,12 @@ function toConversationArtifact(
|
|
|
229
232
|
};
|
|
230
233
|
}
|
|
231
234
|
|
|
235
|
+
function recordValue(value: unknown): Record<string, unknown> | undefined {
|
|
236
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
237
|
+
? (value as Record<string, unknown>)
|
|
238
|
+
: undefined;
|
|
239
|
+
}
|
|
240
|
+
|
|
232
241
|
function preview(value: unknown): string | undefined {
|
|
233
242
|
if (value === undefined || value === null) return undefined;
|
|
234
243
|
const text =
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
|
|
3
|
+
import type { ActionChatUIConfig } from "../../action-ui.js";
|
|
3
4
|
import type { AgentMcpAppPayload } from "../../mcp-client/app-result.js";
|
|
4
5
|
|
|
5
6
|
export interface AgentConversationAttachment {
|
|
@@ -23,8 +24,11 @@ export interface AgentConversationToolCall {
|
|
|
23
24
|
name: string;
|
|
24
25
|
state: AgentConversationToolState;
|
|
25
26
|
input?: string;
|
|
27
|
+
args?: Record<string, unknown>;
|
|
26
28
|
result?: string;
|
|
29
|
+
resultJson?: unknown;
|
|
27
30
|
mcpApp?: AgentMcpAppPayload;
|
|
31
|
+
chatUI?: ActionChatUIConfig;
|
|
28
32
|
summary?: string;
|
|
29
33
|
}
|
|
30
34
|
|