@agent-native/core 0.144.1 → 0.144.2
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/client/analytics/index.d.ts +1 -1
- package/dist/client/analytics/index.js +1 -1
- package/dist/client/analytics-session.d.ts +13 -0
- package/dist/client/analytics-session.js +57 -0
- package/dist/client/analytics.d.ts +1 -0
- package/dist/client/analytics.js +1 -0
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/track.js +7 -0
- package/dist/client/use-action.js +10 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/notifications/routes.d.ts +4 -4
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/action-routes.js +4 -2
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-run-context.d.ts +4 -2
- package/dist/server/agent-run-context.js +5 -3
- package/dist/server/core-routes-plugin.js +2 -0
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +5 -1
- package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +20 -3
- package/dist/tracking/index.d.ts +1 -1
- package/dist/tracking/providers.js +12 -1
- package/dist/tracking/registry.d.ts +12 -2
- package/dist/tracking/registry.js +27 -3
- package/dist/tracking/types.d.ts +8 -0
- package/docs/content/observability.mdx +16 -0
- package/docs/content/tracking.mdx +17 -2
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +5 -1
- package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +20 -3
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureClientException, captureError, captureException, captureMessage, configureTracking, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, getSessionReplayContext, getSessionReplayUrl, isErrorCaptureInstalled, maybeStartSessionReplay, setSentryUser, setTrackingContentCaptureEnabled, setTrackingIdentity, startSessionReplay, stopSessionReplay, trackAgentChatLifecycle, trackEvent, trackSessionStatus, type AgentChatLifecycleEvent, type CaptureExceptionContext, type CapturedExceptionEvent, type ClientCaptureContext, type ConfigureTrackingOptions, type ErrorCaptureConfigOptions, type ExceptionBreadcrumb, type ExceptionLevel, type FirstTouchAttribution, type SessionReplayContext, type SessionReplayLinkOptions, type SessionReplayOptions, type SessionReplayStartResult, type SessionReplayUrlMatcher, type TrackingIdentityUser, } from "../analytics.js";
|
|
1
|
+
export { AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureClientException, captureError, captureException, captureMessage, clearAnalyticsSessionId, configureTracking, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, getSessionReplayContext, getSessionReplayUrl, isErrorCaptureInstalled, maybeStartSessionReplay, setAnalyticsSessionId, setSentryUser, setTrackingContentCaptureEnabled, setTrackingIdentity, startSessionReplay, stopSessionReplay, trackAgentChatLifecycle, trackEvent, trackSessionStatus, type AgentChatLifecycleEvent, type CaptureExceptionContext, type CapturedExceptionEvent, type ClientCaptureContext, type ConfigureTrackingOptions, type ErrorCaptureConfigOptions, type ExceptionBreadcrumb, type ExceptionLevel, type FirstTouchAttribution, type SessionReplayContext, type SessionReplayLinkOptions, type SessionReplayOptions, type SessionReplayStartResult, type SessionReplayUrlMatcher, type TrackingIdentityUser, } from "../analytics.js";
|
|
2
2
|
export { track } from "../track.js";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureClientException, captureError, captureException, captureMessage, configureTracking, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, getSessionReplayContext, getSessionReplayUrl, isErrorCaptureInstalled, maybeStartSessionReplay, setSentryUser, setTrackingContentCaptureEnabled, setTrackingIdentity, startSessionReplay, stopSessionReplay, trackAgentChatLifecycle, trackEvent, trackSessionStatus, } from "../analytics.js";
|
|
1
|
+
export { AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureClientException, captureError, captureException, captureMessage, clearAnalyticsSessionId, configureTracking, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, getSessionReplayContext, getSessionReplayUrl, isErrorCaptureInstalled, maybeStartSessionReplay, setAnalyticsSessionId, setSentryUser, setTrackingContentCaptureEnabled, setTrackingIdentity, startSessionReplay, stopSessionReplay, trackAgentChatLifecycle, trackEvent, trackSessionStatus, } from "../analytics.js";
|
|
2
2
|
export { track } from "../track.js";
|
|
@@ -1,2 +1,15 @@
|
|
|
1
1
|
export declare function getOrCreateAnalyticsAnonymousId(): string | undefined;
|
|
2
|
+
/**
|
|
3
|
+
* Pin the browser session id an app wants every analytics event, agent run, and
|
|
4
|
+
* action request in this tab to carry. Returns the stored id, or `undefined`
|
|
5
|
+
* when there is no browser storage to pin it in (SSR) — the caller can tell
|
|
6
|
+
* those apart.
|
|
7
|
+
*
|
|
8
|
+
* Throws on an id the transport cannot carry rather than storing it.
|
|
9
|
+
*/
|
|
10
|
+
export declare function setAnalyticsSessionId(sessionId: string): string | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Drop a pinned id and let the next read start a fresh rotating session.
|
|
13
|
+
*/
|
|
14
|
+
export declare function clearAnalyticsSessionId(): void;
|
|
2
15
|
export declare function getOrCreateAnalyticsSessionId(): string | undefined;
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { serializeAnalyticsAnonymousIdCookie } from "../shared/analytics-anonymous-id.js";
|
|
2
2
|
const ANONYMOUS_ID_STORAGE_KEY = "agent-native.anonymous_id";
|
|
3
3
|
const SESSION_ID_STORAGE_KEY = "agent-native.session_id";
|
|
4
|
+
const SESSION_ID_PIN_STORAGE_KEY = "agent-native.session_id_pin";
|
|
4
5
|
const SESSION_LAST_ACTIVITY_STORAGE_KEY = "agent-native.session_last_activity";
|
|
5
6
|
// 30-minute idle timeout matches GA4 / Mixpanel defaults: a tab left open
|
|
6
7
|
// overnight starts a new session in the morning instead of stretching one visit.
|
|
7
8
|
const SESSION_IDLE_TIMEOUT_MS = 30 * 60 * 1000;
|
|
9
|
+
// The server drops the session header at 128 characters
|
|
10
|
+
// (`readBrowserSessionIdHeader`), and `fetch` throws on a header value with
|
|
11
|
+
// non-ASCII or newline bytes. Both are rejected at the point the id is set so a
|
|
12
|
+
// bad id fails once, loudly, instead of silently unlinking every later request.
|
|
13
|
+
const MAX_SESSION_ID_LENGTH = 127;
|
|
14
|
+
const SAFE_SESSION_ID = /^[!-~]+$/;
|
|
8
15
|
function generateVisitorId() {
|
|
9
16
|
try {
|
|
10
17
|
if (typeof crypto !== "undefined" &&
|
|
@@ -35,6 +42,15 @@ function safeStorageSet(key, value) {
|
|
|
35
42
|
// private browsing / storage disabled -- best-effort
|
|
36
43
|
}
|
|
37
44
|
}
|
|
45
|
+
function safeStorageRemove(key) {
|
|
46
|
+
try {
|
|
47
|
+
window.localStorage.removeItem(key);
|
|
48
|
+
// coercion-ok: storage that refuses writes holds no key to clear, so the caller's end state already holds
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// private browsing / storage disabled -- best-effort
|
|
52
|
+
}
|
|
53
|
+
}
|
|
38
54
|
function syncAnalyticsAnonymousIdCookie(id) {
|
|
39
55
|
try {
|
|
40
56
|
const cookie = serializeAnalyticsAnonymousIdCookie(id);
|
|
@@ -57,10 +73,51 @@ export function getOrCreateAnalyticsAnonymousId() {
|
|
|
57
73
|
syncAnalyticsAnonymousIdCookie(id);
|
|
58
74
|
return id;
|
|
59
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Pin the browser session id an app wants every analytics event, agent run, and
|
|
78
|
+
* action request in this tab to carry. Returns the stored id, or `undefined`
|
|
79
|
+
* when there is no browser storage to pin it in (SSR) — the caller can tell
|
|
80
|
+
* those apart.
|
|
81
|
+
*
|
|
82
|
+
* Throws on an id the transport cannot carry rather than storing it.
|
|
83
|
+
*/
|
|
84
|
+
export function setAnalyticsSessionId(sessionId) {
|
|
85
|
+
const trimmed = typeof sessionId === "string" ? sessionId.trim() : "";
|
|
86
|
+
if (!trimmed ||
|
|
87
|
+
trimmed.length > MAX_SESSION_ID_LENGTH ||
|
|
88
|
+
!SAFE_SESSION_ID.test(trimmed)) {
|
|
89
|
+
throw new Error(`Invalid analytics session id: expected 1-${MAX_SESSION_ID_LENGTH} printable ASCII characters with no whitespace`);
|
|
90
|
+
}
|
|
91
|
+
if (typeof window === "undefined")
|
|
92
|
+
return undefined;
|
|
93
|
+
safeStorageSet(SESSION_ID_PIN_STORAGE_KEY, trimmed);
|
|
94
|
+
safeStorageSet(SESSION_ID_STORAGE_KEY, trimmed);
|
|
95
|
+
safeStorageSet(SESSION_LAST_ACTIVITY_STORAGE_KEY, String(Date.now()));
|
|
96
|
+
return trimmed;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Drop a pinned id and let the next read start a fresh rotating session.
|
|
100
|
+
*/
|
|
101
|
+
export function clearAnalyticsSessionId() {
|
|
102
|
+
if (typeof window === "undefined")
|
|
103
|
+
return;
|
|
104
|
+
safeStorageRemove(SESSION_ID_PIN_STORAGE_KEY);
|
|
105
|
+
safeStorageRemove(SESSION_ID_STORAGE_KEY);
|
|
106
|
+
safeStorageRemove(SESSION_LAST_ACTIVITY_STORAGE_KEY);
|
|
107
|
+
}
|
|
60
108
|
export function getOrCreateAnalyticsSessionId() {
|
|
61
109
|
if (typeof window === "undefined")
|
|
62
110
|
return undefined;
|
|
63
111
|
const now = Date.now();
|
|
112
|
+
// A pinned id opts out of idle rotation: the app chose it to correlate its
|
|
113
|
+
// own runs, so expiring it mid-workflow would split one correlated session
|
|
114
|
+
// into two that nothing can join back together.
|
|
115
|
+
const pinned = safeStorageGet(SESSION_ID_PIN_STORAGE_KEY);
|
|
116
|
+
if (pinned) {
|
|
117
|
+
safeStorageSet(SESSION_ID_STORAGE_KEY, pinned);
|
|
118
|
+
safeStorageSet(SESSION_LAST_ACTIVITY_STORAGE_KEY, String(now));
|
|
119
|
+
return pinned;
|
|
120
|
+
}
|
|
64
121
|
const lastActivityRaw = safeStorageGet(SESSION_LAST_ACTIVITY_STORAGE_KEY);
|
|
65
122
|
const lastActivity = lastActivityRaw
|
|
66
123
|
? Number.parseInt(lastActivityRaw, 10)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { clearAnalyticsSessionId, setAnalyticsSessionId, } from "./analytics-session.js";
|
|
1
2
|
import type { SessionReplayOptions, SessionReplayStartResult } from "./session-replay.js";
|
|
2
3
|
export { scrubUrl } from "./url-scrub.js";
|
|
3
4
|
export { addErrorBreadcrumb, captureException, captureMessage, isErrorCaptureInstalled, type CaptureExceptionContext, type CapturedExceptionEvent, type ExceptionBreadcrumb, type ExceptionLevel, } from "./error-capture.js";
|
package/dist/client/analytics.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { llmConnectionTrackingProperties, } from "../shared/llm-connection.js";
|
|
2
2
|
import { toPostHogExceptionProperties } from "../tracking/posthog-exception.js";
|
|
3
3
|
import { getOrCreateAnalyticsAnonymousId, getOrCreateAnalyticsSessionId, } from "./analytics-session.js";
|
|
4
|
+
export { clearAnalyticsSessionId, setAnalyticsSessionId, } from "./analytics-session.js";
|
|
4
5
|
import { fetchAgentEngineStatus, fetchAuthSessionStatus, } from "./client-status-requests.js";
|
|
5
6
|
import { installErrorCapture, } from "./error-capture.js";
|
|
6
7
|
import { isDynamicImportFailureMessage } from "./route-chunk-recovery.js";
|
package/dist/client/index.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export { DevDatabaseLink, type DevDatabaseLinkProps, } from "./db-admin/DevDatab
|
|
|
18
18
|
export { DbAdminPage } from "./db-admin/DbAdminPage.js";
|
|
19
19
|
export { installRouteChunkRecovery, reloadForStaleChunk, recoverFromStaleChunkError, } from "./route-chunk-recovery.js";
|
|
20
20
|
export { AgentTerminal, type AgentTerminalProps } from "./terminal/index.js";
|
|
21
|
-
export { trackEvent, trackAgentChatLifecycle, trackSessionStatus, configureTracking, setTrackingContentCaptureEnabled, maybeStartSessionReplay, startSessionReplay, stopSessionReplay, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, setSentryUser, setTrackingIdentity, getSessionReplayContext, getSessionReplayUrl, captureError, captureClientException, AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureException, captureMessage, isErrorCaptureInstalled, type CaptureExceptionContext, type CapturedExceptionEvent, type ClientCaptureContext, type AgentChatLifecycleEvent, type ConfigureTrackingOptions, type ErrorCaptureConfigOptions, type ExceptionBreadcrumb, type ExceptionLevel, type FirstTouchAttribution, type SessionReplayOptions, type SessionReplayContext, type SessionReplayLinkOptions, type SessionReplayStartResult, type SessionReplayUrlMatcher, type TrackingIdentityUser, } from "./analytics.js";
|
|
21
|
+
export { trackEvent, trackAgentChatLifecycle, trackSessionStatus, configureTracking, setTrackingContentCaptureEnabled, maybeStartSessionReplay, startSessionReplay, stopSessionReplay, getAnalyticsAnonymousId, getAnalyticsSessionId, setAnalyticsSessionId, clearAnalyticsSessionId, getFirstTouchAttribution, setSentryUser, setTrackingIdentity, getSessionReplayContext, getSessionReplayUrl, captureError, captureClientException, AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureException, captureMessage, isErrorCaptureInstalled, type CaptureExceptionContext, type CapturedExceptionEvent, type ClientCaptureContext, type AgentChatLifecycleEvent, type ConfigureTrackingOptions, type ErrorCaptureConfigOptions, type ExceptionBreadcrumb, type ExceptionLevel, type FirstTouchAttribution, type SessionReplayOptions, type SessionReplayContext, type SessionReplayLinkOptions, type SessionReplayStartResult, type SessionReplayUrlMatcher, type TrackingIdentityUser, } from "./analytics.js";
|
|
22
22
|
export { track } from "./track.js";
|
|
23
23
|
export { useCollaborativeDoc, isReconcileLeadClient, emailToColor, emailToName, dedupeCollabUsersByEmail, type UseCollaborativeDocOptions, type UseCollaborativeDocResult, type CollabUser, } from "../collab/client.js";
|
|
24
24
|
export { AGENT_CLIENT_ID } from "../collab/agent-identity.js";
|
package/dist/client/index.js
CHANGED
|
@@ -18,7 +18,7 @@ export { DevDatabaseLink, } from "./db-admin/DevDatabaseLink.js";
|
|
|
18
18
|
export { DbAdminPage } from "./db-admin/DbAdminPage.js";
|
|
19
19
|
export { installRouteChunkRecovery, reloadForStaleChunk, recoverFromStaleChunkError, } from "./route-chunk-recovery.js";
|
|
20
20
|
export { AgentTerminal } from "./terminal/index.js";
|
|
21
|
-
export { trackEvent, trackAgentChatLifecycle, trackSessionStatus, configureTracking, setTrackingContentCaptureEnabled, maybeStartSessionReplay, startSessionReplay, stopSessionReplay, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, setSentryUser, setTrackingIdentity, getSessionReplayContext, getSessionReplayUrl, captureError, captureClientException,
|
|
21
|
+
export { trackEvent, trackAgentChatLifecycle, trackSessionStatus, configureTracking, setTrackingContentCaptureEnabled, maybeStartSessionReplay, startSessionReplay, stopSessionReplay, getAnalyticsAnonymousId, getAnalyticsSessionId, setAnalyticsSessionId, clearAnalyticsSessionId, getFirstTouchAttribution, setSentryUser, setTrackingIdentity, getSessionReplayContext, getSessionReplayUrl, captureError, captureClientException,
|
|
22
22
|
// First-party, Sentry-style error capture (auto + manual API).
|
|
23
23
|
AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureException, captureMessage, isErrorCaptureInstalled, } from "./analytics.js";
|
|
24
24
|
export { track } from "./track.js";
|
package/dist/client/track.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getOrCreateAnalyticsSessionId } from "./analytics-session.js";
|
|
1
2
|
import { agentNativePath } from "./api-path.js";
|
|
2
3
|
/**
|
|
3
4
|
* Fire an analytics event from browser/app code. This is the client-side twin
|
|
@@ -35,6 +36,7 @@ export function track(name, properties) {
|
|
|
35
36
|
// Non-serializable properties — drop rather than throw into the caller.
|
|
36
37
|
return Promise.resolve();
|
|
37
38
|
}
|
|
39
|
+
const browserSessionId = getOrCreateAnalyticsSessionId();
|
|
38
40
|
return fetch(agentNativePath("/_agent-native/track"), {
|
|
39
41
|
method: "POST",
|
|
40
42
|
headers: {
|
|
@@ -43,6 +45,11 @@ export function track(name, properties) {
|
|
|
43
45
|
// middleware trusts it as a first-party marker. Matches the convention
|
|
44
46
|
// used by other client writes (application-state, guided-questions).
|
|
45
47
|
"X-Agent-Native-CSRF": "1",
|
|
48
|
+
// Same session the action client and agent chat send, so a client event
|
|
49
|
+
// and the server events from the same visit share one session.
|
|
50
|
+
...(browserSessionId
|
|
51
|
+
? { "X-Agent-Native-Session-Id": browserSessionId }
|
|
52
|
+
: {}),
|
|
46
53
|
},
|
|
47
54
|
body,
|
|
48
55
|
keepalive: true,
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* compatibility.
|
|
23
23
|
*/
|
|
24
24
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
25
|
+
import { getOrCreateAnalyticsSessionId } from "./analytics-session.js";
|
|
25
26
|
import { trackEvent } from "./analytics.js";
|
|
26
27
|
import { agentNativePath } from "./api-path.js";
|
|
27
28
|
import { getBrowserTabId } from "./browser-tab-id.js";
|
|
@@ -192,8 +193,16 @@ async function performActionFetch(name, method, params, options) {
|
|
|
192
193
|
if (buildId)
|
|
193
194
|
headers["X-Agent-Native-Build-Id"] = buildId;
|
|
194
195
|
const tz = resolveUserTimezone();
|
|
195
|
-
if (tz)
|
|
196
|
+
if (tz) {
|
|
196
197
|
headers["x-user-timezone"] = tz;
|
|
198
|
+
}
|
|
199
|
+
// Same browser session the agent chat sends on an agent run, so an action
|
|
200
|
+
// called from the UI and an action the agent calls during that visit land on
|
|
201
|
+
// one `$session_id` in traces and session replay.
|
|
202
|
+
const browserSessionId = getOrCreateAnalyticsSessionId();
|
|
203
|
+
if (browserSessionId) {
|
|
204
|
+
headers["X-Agent-Native-Session-Id"] = browserSessionId;
|
|
205
|
+
}
|
|
197
206
|
const init = {
|
|
198
207
|
method,
|
|
199
208
|
headers,
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
|
|
|
26
26
|
* Body: { update: string (base64), requestSource?: string }
|
|
27
27
|
*/
|
|
28
28
|
export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
29
|
-
error: string;
|
|
30
29
|
ok?: undefined;
|
|
30
|
+
error: string;
|
|
31
31
|
} | {
|
|
32
32
|
error?: undefined;
|
|
33
33
|
ok: boolean;
|
|
@@ -17,11 +17,11 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
17
17
|
id?: undefined;
|
|
18
18
|
provider?: undefined;
|
|
19
19
|
} | {
|
|
20
|
-
error?: undefined;
|
|
21
20
|
configured?: undefined;
|
|
22
21
|
connectPath?: undefined;
|
|
23
22
|
url: string;
|
|
24
23
|
id: string;
|
|
25
24
|
provider: string;
|
|
25
|
+
error?: undefined;
|
|
26
26
|
}>;
|
|
27
27
|
export default _default;
|
|
@@ -137,13 +137,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
137
137
|
inputSchema: {
|
|
138
138
|
type: string;
|
|
139
139
|
properties: {
|
|
140
|
-
count?: undefined;
|
|
141
140
|
query?: undefined;
|
|
142
141
|
minutes?: undefined;
|
|
143
142
|
limit?: undefined;
|
|
144
143
|
clientHint?: undefined;
|
|
145
144
|
timestamp?: undefined;
|
|
146
145
|
chapterId?: undefined;
|
|
146
|
+
count?: undefined;
|
|
147
147
|
startAt?: undefined;
|
|
148
148
|
endAt?: undefined;
|
|
149
149
|
reason?: undefined;
|
|
@@ -159,7 +159,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
159
159
|
inputSchema: {
|
|
160
160
|
type: string;
|
|
161
161
|
properties: {
|
|
162
|
-
count?: undefined;
|
|
163
162
|
query: {
|
|
164
163
|
type: string;
|
|
165
164
|
description: string;
|
|
@@ -175,6 +174,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
175
174
|
clientHint?: undefined;
|
|
176
175
|
timestamp?: undefined;
|
|
177
176
|
chapterId?: undefined;
|
|
177
|
+
count?: undefined;
|
|
178
178
|
startAt?: undefined;
|
|
179
179
|
endAt?: undefined;
|
|
180
180
|
reason?: undefined;
|
|
@@ -190,7 +190,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
190
190
|
inputSchema: {
|
|
191
191
|
type: string;
|
|
192
192
|
properties: {
|
|
193
|
-
count?: undefined;
|
|
194
193
|
minutes: {
|
|
195
194
|
type: string;
|
|
196
195
|
description: string;
|
|
@@ -200,6 +199,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
200
199
|
clientHint?: undefined;
|
|
201
200
|
timestamp?: undefined;
|
|
202
201
|
chapterId?: undefined;
|
|
202
|
+
count?: undefined;
|
|
203
203
|
startAt?: undefined;
|
|
204
204
|
endAt?: undefined;
|
|
205
205
|
reason?: undefined;
|
|
@@ -216,7 +216,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
216
216
|
type: string;
|
|
217
217
|
required: string[];
|
|
218
218
|
properties: {
|
|
219
|
-
count?: undefined;
|
|
220
219
|
query: {
|
|
221
220
|
type: string;
|
|
222
221
|
description: string;
|
|
@@ -235,6 +234,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
235
234
|
};
|
|
236
235
|
timestamp?: undefined;
|
|
237
236
|
chapterId?: undefined;
|
|
237
|
+
count?: undefined;
|
|
238
238
|
startAt?: undefined;
|
|
239
239
|
endAt?: undefined;
|
|
240
240
|
reason?: undefined;
|
|
@@ -250,7 +250,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
250
250
|
type: string;
|
|
251
251
|
required: string[];
|
|
252
252
|
properties: {
|
|
253
|
-
count?: undefined;
|
|
254
253
|
query?: undefined;
|
|
255
254
|
minutes?: undefined;
|
|
256
255
|
limit?: undefined;
|
|
@@ -264,6 +263,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
264
263
|
description: string;
|
|
265
264
|
};
|
|
266
265
|
chapterId?: undefined;
|
|
266
|
+
count?: undefined;
|
|
267
267
|
startAt?: undefined;
|
|
268
268
|
endAt?: undefined;
|
|
269
269
|
includeMicrophone?: undefined;
|
|
@@ -314,13 +314,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
314
314
|
type: string;
|
|
315
315
|
required: string[];
|
|
316
316
|
properties: {
|
|
317
|
-
count?: undefined;
|
|
318
317
|
query?: undefined;
|
|
319
318
|
minutes?: undefined;
|
|
320
319
|
limit?: undefined;
|
|
321
320
|
clientHint?: undefined;
|
|
322
321
|
timestamp?: undefined;
|
|
323
322
|
chapterId?: undefined;
|
|
323
|
+
count?: undefined;
|
|
324
324
|
startAt: {
|
|
325
325
|
type: string;
|
|
326
326
|
description: string;
|
|
@@ -349,13 +349,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
349
349
|
type: string;
|
|
350
350
|
required: string[];
|
|
351
351
|
properties: {
|
|
352
|
-
count?: undefined;
|
|
353
352
|
query?: undefined;
|
|
354
353
|
minutes?: undefined;
|
|
355
354
|
limit?: undefined;
|
|
356
355
|
clientHint?: undefined;
|
|
357
356
|
timestamp?: undefined;
|
|
358
357
|
chapterId?: undefined;
|
|
358
|
+
count?: undefined;
|
|
359
359
|
startAt?: undefined;
|
|
360
360
|
endAt?: undefined;
|
|
361
361
|
reason?: undefined;
|
|
@@ -16,18 +16,18 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
|
|
|
16
16
|
error?: undefined;
|
|
17
17
|
ok?: undefined;
|
|
18
18
|
} | {
|
|
19
|
-
count?: undefined;
|
|
20
19
|
updated: number;
|
|
21
20
|
error?: undefined;
|
|
22
21
|
ok?: undefined;
|
|
23
|
-
} | {
|
|
24
22
|
count?: undefined;
|
|
23
|
+
} | {
|
|
25
24
|
updated?: undefined;
|
|
26
25
|
error: string;
|
|
27
26
|
ok?: undefined;
|
|
28
|
-
} | {
|
|
29
27
|
count?: undefined;
|
|
28
|
+
} | {
|
|
30
29
|
updated?: undefined;
|
|
31
|
-
ok: boolean;
|
|
32
30
|
error?: undefined;
|
|
31
|
+
ok: boolean;
|
|
32
|
+
count?: undefined;
|
|
33
33
|
}>>;
|
|
@@ -42,22 +42,22 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
44
|
error?: undefined;
|
|
45
|
-
ok?: undefined;
|
|
46
45
|
summary: import("./types.js").TraceSummary;
|
|
47
46
|
spans: import("./types.js").TraceSpan[];
|
|
48
47
|
id?: undefined;
|
|
48
|
+
ok?: undefined;
|
|
49
49
|
} | {
|
|
50
50
|
error?: undefined;
|
|
51
|
-
ok?: undefined;
|
|
52
51
|
summary?: undefined;
|
|
53
52
|
spans?: undefined;
|
|
54
53
|
id: string;
|
|
55
|
-
} | {
|
|
56
54
|
ok?: undefined;
|
|
55
|
+
} | {
|
|
57
56
|
summary?: undefined;
|
|
58
57
|
spans?: undefined;
|
|
59
58
|
id?: undefined;
|
|
60
59
|
error: any;
|
|
60
|
+
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
62
|
error?: undefined;
|
|
63
63
|
summary?: undefined;
|
|
@@ -75,8 +75,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
75
75
|
user: "user";
|
|
76
76
|
}>>;
|
|
77
77
|
}, z.core.$strip>>, {
|
|
78
|
-
id?: undefined;
|
|
79
78
|
deleted?: undefined;
|
|
79
|
+
id?: undefined;
|
|
80
80
|
providers: {
|
|
81
81
|
id: string;
|
|
82
82
|
label: string;
|
|
@@ -93,9 +93,9 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
93
93
|
label?: undefined;
|
|
94
94
|
message?: undefined;
|
|
95
95
|
} | {
|
|
96
|
-
id?: undefined;
|
|
97
96
|
deleted?: undefined;
|
|
98
97
|
count?: undefined;
|
|
98
|
+
id?: undefined;
|
|
99
99
|
providers?: undefined;
|
|
100
100
|
found: boolean;
|
|
101
101
|
provider: import("../custom-registry.js").CustomProviderConfig;
|
|
@@ -48,8 +48,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
|
|
|
48
48
|
}>;
|
|
49
49
|
/** DELETE /_agent-native/resources/:id — delete a resource */
|
|
50
50
|
export declare function handleDeleteResource(event: any): Promise<{
|
|
51
|
-
ok?: undefined;
|
|
52
51
|
error: string;
|
|
52
|
+
ok?: undefined;
|
|
53
53
|
} | {
|
|
54
54
|
error?: undefined;
|
|
55
55
|
ok: boolean;
|
package/dist/secrets/routes.d.ts
CHANGED
|
@@ -34,16 +34,16 @@ export declare function createListSecretsHandler(): import("h3").EventHandlerWit
|
|
|
34
34
|
/** POST /_agent-native/secrets/:key — write a secret. */
|
|
35
35
|
export declare function createWriteSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
36
36
|
error: string;
|
|
37
|
-
ok?: undefined;
|
|
38
37
|
status?: undefined;
|
|
38
|
+
ok?: undefined;
|
|
39
39
|
} | {
|
|
40
40
|
ok: boolean;
|
|
41
41
|
status: string;
|
|
42
42
|
error?: undefined;
|
|
43
43
|
} | {
|
|
44
|
-
ok?: undefined;
|
|
45
44
|
error: string;
|
|
46
45
|
removed?: undefined;
|
|
46
|
+
ok?: undefined;
|
|
47
47
|
} | {
|
|
48
48
|
ok: boolean;
|
|
49
49
|
removed: boolean;
|
|
@@ -54,9 +54,9 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
|
|
|
54
54
|
* or the current stored value without changing anything.
|
|
55
55
|
*/
|
|
56
56
|
export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
57
|
-
ok?: undefined;
|
|
58
57
|
error: string;
|
|
59
58
|
note?: undefined;
|
|
59
|
+
ok?: undefined;
|
|
60
60
|
} | {
|
|
61
61
|
ok: boolean;
|
|
62
62
|
note?: undefined;
|
|
@@ -7,7 +7,7 @@ import { readBody } from "../server/h3-helpers.js";
|
|
|
7
7
|
import { EMBED_TARGET_HEADER } from "../shared/embed-auth.js";
|
|
8
8
|
import { isMcpEmbedCorsOrigin, MCP_EMBED_CORS_ALLOW_HEADERS, shouldAllowMcpEmbedCredentials, } from "../shared/mcp-embed-headers.js";
|
|
9
9
|
import { actionCallIsReadOnly, notifyActionChange } from "./action-change.js";
|
|
10
|
-
import { seedAgentRunOwnerContext, } from "./agent-run-context.js";
|
|
10
|
+
import { readBrowserSessionIdHeader, seedAgentRunOwnerContext, } from "./agent-run-context.js";
|
|
11
11
|
import { captureError } from "./capture-error.js";
|
|
12
12
|
import { getAllowedCorsOrigin as resolveAllowedCorsOrigin, readCorsAllowedOrigins, } from "./cors-origins.js";
|
|
13
13
|
import { resolveEmbedSessionFromRequest, resolvedEmbedCapabilityScope, } from "./embed-session.js";
|
|
@@ -158,7 +158,7 @@ function handleOptionsRequest(event) {
|
|
|
158
158
|
}
|
|
159
159
|
setResponseHeader(event, "Access-Control-Allow-Methods", "GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS");
|
|
160
160
|
setResponseHeader(event, "Access-Control-Allow-Headers", cors.credentials
|
|
161
|
-
? `Content-Type,Authorization,X-Requested-With,X-Request-Source,X-Agent-Native-CSRF,X-User-Timezone,X-Agent-Native-Tool-Bridge,X-Agent-Native-Tool-Id,X-Agent-Native-Frontend,X-Agent-Native-Client-Compatibility,X-Agent-Native-Build-Id,${EMBED_TARGET_HEADER}`
|
|
161
|
+
? `Content-Type,Authorization,X-Requested-With,X-Request-Source,X-Agent-Native-CSRF,X-User-Timezone,X-Agent-Native-Session-Id,X-Agent-Native-Tool-Bridge,X-Agent-Native-Tool-Id,X-Agent-Native-Frontend,X-Agent-Native-Client-Compatibility,X-Agent-Native-Build-Id,${EMBED_TARGET_HEADER}`
|
|
162
162
|
: `${MCP_EMBED_CORS_ALLOW_HEADERS},X-Agent-Native-Tool-Bridge,X-Agent-Native-Tool-Id,X-Agent-Native-Frontend,X-Agent-Native-Client-Compatibility,X-Agent-Native-Build-Id`);
|
|
163
163
|
}
|
|
164
164
|
setResponseStatus(event, 204);
|
|
@@ -350,12 +350,14 @@ export function mountActionRoutes(nitroApp, actions, options) {
|
|
|
350
350
|
: undefined;
|
|
351
351
|
}
|
|
352
352
|
const timezone = readTimezoneHeader(event);
|
|
353
|
+
const browserSessionId = readBrowserSessionIdHeader(event);
|
|
353
354
|
return runWithRequestContext({
|
|
354
355
|
userEmail,
|
|
355
356
|
userName,
|
|
356
357
|
orgId,
|
|
357
358
|
authCapability,
|
|
358
359
|
timezone,
|
|
360
|
+
browserSessionId,
|
|
359
361
|
requestOrigin: getRequestURL(event).origin,
|
|
360
362
|
// Captured here because this is the last layer that still holds
|
|
361
363
|
// the h3 event; everything below reads it off the request store.
|
|
@@ -27,10 +27,10 @@ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scop
|
|
|
27
27
|
export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
28
28
|
error: any;
|
|
29
29
|
} | {
|
|
30
|
-
error?: undefined;
|
|
31
30
|
ok: boolean;
|
|
32
31
|
key: string;
|
|
33
32
|
baseUrlKey?: string;
|
|
34
33
|
scope: AgentEngineApiKeyScope;
|
|
34
|
+
error?: undefined;
|
|
35
35
|
}>>;
|
|
36
36
|
export {};
|
|
@@ -14,9 +14,11 @@ export declare function readAgentRunTimezone(event: H3Event): string | undefined
|
|
|
14
14
|
*
|
|
15
15
|
* Emitted as PostHog's `$session_id` on the run's `$ai_*` events so an agent
|
|
16
16
|
* trace joins to the session replay it happened in. Distinct from
|
|
17
|
-
* `$ai_session_id`, which is the conversation thread.
|
|
17
|
+
* `$ai_session_id`, which is the conversation thread. Read by both the agent
|
|
18
|
+
* run path and the HTTP action route, so one visit correlates across the UI's
|
|
19
|
+
* action calls and the agent's.
|
|
18
20
|
*/
|
|
19
|
-
export declare function
|
|
21
|
+
export declare function readBrowserSessionIdHeader(event: H3Event): string | undefined;
|
|
20
22
|
export declare function seedAgentRunOwnerContext(event: H3Event, ownerContext: AgentRunOwnerContext): AgentRunOwnerContext;
|
|
21
23
|
export declare function seedBackgroundAgentRunOwnerContext(event: H3Event, runId: string): Promise<AgentRunOwnerContext | null>;
|
|
22
24
|
export declare function resolveAgentRunOwnerContext(event: H3Event, options?: {
|
|
@@ -49,9 +49,11 @@ export function readAgentRunTimezone(event) {
|
|
|
49
49
|
*
|
|
50
50
|
* Emitted as PostHog's `$session_id` on the run's `$ai_*` events so an agent
|
|
51
51
|
* trace joins to the session replay it happened in. Distinct from
|
|
52
|
-
* `$ai_session_id`, which is the conversation thread.
|
|
52
|
+
* `$ai_session_id`, which is the conversation thread. Read by both the agent
|
|
53
|
+
* run path and the HTTP action route, so one visit correlates across the UI's
|
|
54
|
+
* action calls and the agent's.
|
|
53
55
|
*/
|
|
54
|
-
export function
|
|
56
|
+
export function readBrowserSessionIdHeader(event) {
|
|
55
57
|
const raw = readHeaderValue(event, "x-agent-native-session-id");
|
|
56
58
|
const value = Array.isArray(raw) ? raw[0] : raw;
|
|
57
59
|
return typeof value === "string" &&
|
|
@@ -140,7 +142,7 @@ export async function resolveAgentRunOrgId(options) {
|
|
|
140
142
|
export async function resolveAgentRunRequestContext(options) {
|
|
141
143
|
const orgId = await resolveAgentRunOrgId(options);
|
|
142
144
|
const timezone = readAgentRunTimezone(options.event);
|
|
143
|
-
const browserSessionId =
|
|
145
|
+
const browserSessionId = readBrowserSessionIdHeader(options.event);
|
|
144
146
|
const waitUntil = requestWaitUntil(options.event);
|
|
145
147
|
const run = {
|
|
146
148
|
...(options.isBackgroundWorker ? { isBackgroundWorker: true } : {}),
|
|
@@ -33,6 +33,7 @@ import { registerBuiltinProviders } from "../tracking/providers.js";
|
|
|
33
33
|
import { validateTrackPayload } from "../tracking/route.js";
|
|
34
34
|
import { createAutomationsHandler } from "../triggers/routes.js";
|
|
35
35
|
import { createAgentEngineApiKeyHandler } from "./agent-engine-api-key-route.js";
|
|
36
|
+
import { readBrowserSessionIdHeader } from "./agent-run-context.js";
|
|
36
37
|
import { getConfiguredAppBasePath, stripAppBasePath } from "./app-base-path.js";
|
|
37
38
|
import { getAppName } from "./app-name.js";
|
|
38
39
|
import { getSession } from "./auth.js";
|
|
@@ -2544,6 +2545,7 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
2544
2545
|
try {
|
|
2545
2546
|
track(validation.name, properties, {
|
|
2546
2547
|
userId: userEmail,
|
|
2548
|
+
sessionId: readBrowserSessionIdHeader(event),
|
|
2547
2549
|
});
|
|
2548
2550
|
}
|
|
2549
2551
|
catch {
|
|
@@ -26,8 +26,8 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
|
|
|
26
26
|
expiresAt?: undefined;
|
|
27
27
|
ttlSeconds?: undefined;
|
|
28
28
|
} | {
|
|
29
|
-
error?: undefined;
|
|
30
29
|
token: string;
|
|
31
30
|
expiresAt: string;
|
|
32
31
|
ttlSeconds: number;
|
|
32
|
+
error?: undefined;
|
|
33
33
|
}>>;
|
|
@@ -283,7 +283,11 @@ same best-effort fan-out as other tracking events.
|
|
|
283
283
|
upward through `$ai_parent_id` so a backend can rebuild the tree.
|
|
284
284
|
`$ai_session_id` is the thread; the browser session is separate and ships as
|
|
285
285
|
`$session_id`, read from `X-Agent-Native-Session-Id` via
|
|
286
|
-
`RequestContext.browserSessionId`.
|
|
286
|
+
`RequestContext.browserSessionId`. The agent chat and the action client both
|
|
287
|
+
send that header, so a UI action call and the agent's own call during one
|
|
288
|
+
visit share a session. `setAnalyticsSessionId()` from
|
|
289
|
+
`@agent-native/core/client/analytics` pins a custom id and opts it out of the
|
|
290
|
+
30-minute idle rotation. Emission lives in `posthog-ai.ts`.
|
|
287
291
|
- Agent Native Analytics shape: the same event lands in `analytics_events` with
|
|
288
292
|
mirrored query-friendly properties such as `run_id`, `thread_id`,
|
|
289
293
|
`cost_cents_x100`, `duration_ms`, `tool_calls`, `successful_tools`,
|
|
@@ -18,19 +18,26 @@ The tracking system provides a single `track()` call that fans out to all regist
|
|
|
18
18
|
## How It Works
|
|
19
19
|
|
|
20
20
|
1. At server startup, `registerBuiltinProviders()` checks env vars and registers any configured providers.
|
|
21
|
-
2. Application code calls `track(eventName, properties,
|
|
21
|
+
2. Application code calls `track(eventName, properties, source)` from actions, plugins, or server routes.
|
|
22
22
|
3. The registry fans out the event to every registered provider. Errors are caught and logged -- a failing provider never crashes the caller.
|
|
23
23
|
4. Built-in providers batch HTTP calls (flush every 10 seconds or 50 events, whichever comes first).
|
|
24
24
|
|
|
25
25
|
## API
|
|
26
26
|
|
|
27
|
-
### `track(name, properties?,
|
|
27
|
+
### `track(name, properties?, source?)`
|
|
28
28
|
|
|
29
|
-
Fire an analytics event.
|
|
29
|
+
Fire an analytics event. `source` is either a `{ userId, anonymousId, sessionId }`
|
|
30
|
+
meta object or an action's `ctx` passed straight through.
|
|
30
31
|
|
|
31
32
|
```ts
|
|
32
33
|
import { track } from "@agent-native/core/tracking";
|
|
33
34
|
|
|
35
|
+
// From an action — pass ctx; userId comes from ctx.userEmail.
|
|
36
|
+
run: async ({ name }, ctx) => {
|
|
37
|
+
track("meal.logged", { mealName: name, calories: 350 }, ctx);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// From a plugin or route with no ctx.
|
|
34
41
|
track(
|
|
35
42
|
"meal.logged",
|
|
36
43
|
{ mealName: "Salad", calories: 350 },
|
|
@@ -38,6 +45,16 @@ track(
|
|
|
38
45
|
);
|
|
39
46
|
```
|
|
40
47
|
|
|
48
|
+
The caller's browser session comes from the ambient request context
|
|
49
|
+
(`RequestContext.browserSessionId`, set from the `X-Agent-Native-Session-Id`
|
|
50
|
+
header), so it resolves the same whether the UI called the action or the agent
|
|
51
|
+
did. Pass `sessionId` in the meta object to override it — routes that run
|
|
52
|
+
outside a request context, such as `/_agent-native/track`, do exactly that.
|
|
53
|
+
Providers map it to their own session field: `$session_id` for PostHog (which
|
|
54
|
+
joins the event to session replay), `session_id` as a property for Mixpanel and
|
|
55
|
+
Amplitude, a top-level `sessionId` for webhooks and Agent Native Analytics. It
|
|
56
|
+
is absent for callers with no browser — cron, CLI, MCP, A2A.
|
|
57
|
+
|
|
41
58
|
### `identify(userId, traits?)`
|
|
42
59
|
|
|
43
60
|
Identify a user with traits. Forwarded to providers that support it.
|
package/dist/tracking/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { track, identify, flushTracking, registerTrackingProvider, unregisterTrackingProvider, listTrackingProviders, } from "./registry.js";
|
|
1
|
+
export { track, identify, flushTracking, registerTrackingProvider, unregisterTrackingProvider, listTrackingProviders, type TrackingMeta, type TrackingSource, } from "./registry.js";
|
|
2
2
|
export { registerBuiltinProviders } from "./providers.js";
|
|
3
3
|
export { captureException, type TrackingExceptionContext, type TrackingExceptionLevel, } from "./error-capture.js";
|
|
4
4
|
export { errorToPostHogExceptionProperties, parseStackFrames, reshapeTrackedExceptionProperties, toPostHogExceptionProperties, type PostHogExceptionEntry, type PostHogExceptionInput, type PostHogExceptionLevel, type PostHogExceptionProperties, type PostHogStackFrame, } from "./posthog-exception.js";
|
|
@@ -134,6 +134,7 @@ function createPostHogProvider(apiKey, host, errorTracking) {
|
|
|
134
134
|
properties: {
|
|
135
135
|
distinct_id: distinctId,
|
|
136
136
|
...properties,
|
|
137
|
+
...(event.sessionId ? { $session_id: event.sessionId } : {}),
|
|
137
138
|
timestamp: event.timestamp,
|
|
138
139
|
},
|
|
139
140
|
}));
|
|
@@ -168,6 +169,7 @@ function createPostHogProvider(apiKey, host, errorTracking) {
|
|
|
168
169
|
distinct_id: distinctId,
|
|
169
170
|
properties: {
|
|
170
171
|
...event.properties,
|
|
172
|
+
...(event.sessionId ? { $session_id: event.sessionId } : {}),
|
|
171
173
|
timestamp: event.timestamp,
|
|
172
174
|
},
|
|
173
175
|
}));
|
|
@@ -225,6 +227,9 @@ function createMixpanelProvider(token) {
|
|
|
225
227
|
? new Date(event.timestamp).getTime() / 1000
|
|
226
228
|
: undefined,
|
|
227
229
|
...event.properties,
|
|
230
|
+
// Mixpanel's own `$session_id` is numeric and assigned by its SDK, so
|
|
231
|
+
// the browser session lands as a plain property here.
|
|
232
|
+
...(event.sessionId ? { session_id: event.sessionId } : {}),
|
|
228
233
|
},
|
|
229
234
|
};
|
|
230
235
|
enqueue("https://api.mixpanel.com/track", JSON.stringify([data]));
|
|
@@ -253,7 +258,11 @@ function createAmplitudeProvider(apiKey) {
|
|
|
253
258
|
{
|
|
254
259
|
event_type: event.name,
|
|
255
260
|
user_id: event.userId || "anonymous",
|
|
256
|
-
|
|
261
|
+
// Amplitude's top-level `session_id` must be a numeric epoch, so
|
|
262
|
+
// the browser session ships as an event property instead.
|
|
263
|
+
event_properties: event.sessionId
|
|
264
|
+
? { ...event.properties, session_id: event.sessionId }
|
|
265
|
+
: event.properties,
|
|
257
266
|
time: event.timestamp
|
|
258
267
|
? new Date(event.timestamp).getTime()
|
|
259
268
|
: undefined,
|
|
@@ -290,6 +299,7 @@ function createWebhookProvider(url, authHeader) {
|
|
|
290
299
|
event: event.name,
|
|
291
300
|
properties: event.properties,
|
|
292
301
|
userId: event.userId,
|
|
302
|
+
sessionId: event.sessionId,
|
|
293
303
|
timestamp: event.timestamp,
|
|
294
304
|
}), extra);
|
|
295
305
|
},
|
|
@@ -318,6 +328,7 @@ function createAgentNativeAnalyticsProvider(publicKey, endpoint) {
|
|
|
318
328
|
properties: event.properties ?? {},
|
|
319
329
|
userId: event.userId,
|
|
320
330
|
anonymousId: event.anonymousId,
|
|
331
|
+
sessionId: event.sessionId,
|
|
321
332
|
timestamp: event.timestamp,
|
|
322
333
|
}), undefined, { flushImmediately });
|
|
323
334
|
},
|
|
@@ -1,10 +1,20 @@
|
|
|
1
|
+
import type { ActionRunContext } from "../action.js";
|
|
1
2
|
import type { TrackingProvider } from "./types.js";
|
|
2
3
|
export declare function registerTrackingProvider(provider: TrackingProvider): void;
|
|
3
4
|
export declare function unregisterTrackingProvider(name: string): boolean;
|
|
4
5
|
export declare function listTrackingProviders(): string[];
|
|
5
|
-
export
|
|
6
|
+
export interface TrackingMeta {
|
|
6
7
|
userId?: string;
|
|
7
8
|
anonymousId?: string;
|
|
8
|
-
|
|
9
|
+
/** Overrides the ambient request's browser session. */
|
|
10
|
+
sessionId?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Who an event is attributed to. Pass an action's `ctx` straight through —
|
|
14
|
+
* `track("project_created", { template }, ctx)` — instead of restating
|
|
15
|
+
* `{ userId: ctx.userEmail }` at every call site.
|
|
16
|
+
*/
|
|
17
|
+
export type TrackingSource = TrackingMeta | ActionRunContext;
|
|
18
|
+
export declare function track(name: string, properties?: Record<string, unknown>, source?: TrackingSource): void;
|
|
9
19
|
export declare function identify(userId: string, traits?: Record<string, unknown>): void;
|
|
10
20
|
export declare function flushTracking(): Promise<void[]>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getRequestContext } from "../server/request-context.js";
|
|
1
2
|
const REGISTRY_KEY = Symbol.for("@agent-native/core/tracking.registry");
|
|
2
3
|
function getRegistry() {
|
|
3
4
|
const g = globalThis;
|
|
@@ -20,13 +21,36 @@ export function unregisterTrackingProvider(name) {
|
|
|
20
21
|
export function listTrackingProviders() {
|
|
21
22
|
return Array.from(getRegistry().keys());
|
|
22
23
|
}
|
|
23
|
-
|
|
24
|
+
// `caller` is required on ActionRunContext and absent from TrackingMeta, so it
|
|
25
|
+
// is the one field that tells the two apart without the caller declaring which
|
|
26
|
+
// shape it passed.
|
|
27
|
+
function isActionRunContext(source) {
|
|
28
|
+
return typeof source.caller === "string";
|
|
29
|
+
}
|
|
30
|
+
function resolveTrackingSource(source) {
|
|
31
|
+
// The browser session rides the request, not the caller's arguments, so it
|
|
32
|
+
// resolves the same way whether the UI called the action or the agent did.
|
|
33
|
+
const ambientSessionId = getRequestContext()?.browserSessionId;
|
|
34
|
+
if (!source)
|
|
35
|
+
return { sessionId: ambientSessionId };
|
|
36
|
+
if (isActionRunContext(source)) {
|
|
37
|
+
return { userId: source.userEmail, sessionId: ambientSessionId };
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
userId: source.userId,
|
|
41
|
+
anonymousId: source.anonymousId,
|
|
42
|
+
sessionId: source.sessionId ?? ambientSessionId,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export function track(name, properties, source) {
|
|
46
|
+
const { userId, anonymousId, sessionId } = resolveTrackingSource(source);
|
|
24
47
|
const event = {
|
|
25
48
|
name,
|
|
26
49
|
properties,
|
|
27
50
|
timestamp: new Date().toISOString(),
|
|
28
|
-
userId
|
|
29
|
-
anonymousId
|
|
51
|
+
userId,
|
|
52
|
+
anonymousId,
|
|
53
|
+
sessionId,
|
|
30
54
|
};
|
|
31
55
|
for (const provider of getRegistry().values()) {
|
|
32
56
|
try {
|
package/dist/tracking/types.d.ts
CHANGED
|
@@ -4,6 +4,14 @@ export interface TrackingEvent {
|
|
|
4
4
|
timestamp?: string;
|
|
5
5
|
userId?: string;
|
|
6
6
|
anonymousId?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Browser session the event belongs to, so a server-side event joins the
|
|
9
|
+
* visit that caused it. Carried as a typed field rather than a property so
|
|
10
|
+
* each provider maps it to its own session concept instead of every backend
|
|
11
|
+
* receiving another backend's reserved key. `undefined` for callers with no
|
|
12
|
+
* browser — cron, CLI, MCP, A2A — which is a real distinction, not a gap.
|
|
13
|
+
*/
|
|
14
|
+
sessionId?: string;
|
|
7
15
|
}
|
|
8
16
|
export interface TrackingProvider {
|
|
9
17
|
name: string;
|
|
@@ -307,6 +307,22 @@ Nodes share the run id as `$ai_trace_id`, so PostHog renders one tree per run.
|
|
|
307
307
|
`$ai_session_id` is the conversation thread; the browser session is sent
|
|
308
308
|
separately as `$session_id` so a trace links to its session replay.
|
|
309
309
|
|
|
310
|
+
The browser session id travels on the `X-Agent-Native-Session-Id` header. Both
|
|
311
|
+
the agent chat and the action client (`useActionQuery` / `useActionMutation` /
|
|
312
|
+
`callAction`) send it, and the server exposes it to actions and agent runs alike
|
|
313
|
+
as `RequestContext.browserSessionId` — so an action the UI calls and an action
|
|
314
|
+
the agent calls during the same visit correlate. The id rotates after 30 idle
|
|
315
|
+
minutes by default; to correlate a workflow under an id of your own, pin it once
|
|
316
|
+
at startup:
|
|
317
|
+
|
|
318
|
+
```ts
|
|
319
|
+
import { setAnalyticsSessionId } from "@agent-native/core/client/analytics";
|
|
320
|
+
|
|
321
|
+
// Pinned ids opt out of idle rotation. Call clearAnalyticsSessionId() to
|
|
322
|
+
// return to the rotating default.
|
|
323
|
+
setAnalyticsSessionId(myWorkflowId);
|
|
324
|
+
```
|
|
325
|
+
|
|
310
326
|
Tool calls always appear (PostHog derives its tool tags from them), but their
|
|
311
327
|
**arguments** require `captureToolArgs` and message content requires
|
|
312
328
|
`capturePrompts`. When those are off the fields are omitted entirely rather than
|
|
@@ -116,7 +116,7 @@ used as a fallback for it.
|
|
|
116
116
|
|
|
117
117
|
## API {#api}
|
|
118
118
|
|
|
119
|
-
### `track(name, properties?,
|
|
119
|
+
### `track(name, properties?, source?)` {#track}
|
|
120
120
|
|
|
121
121
|
Fire an analytics event. Fans out to all registered providers.
|
|
122
122
|
|
|
@@ -130,6 +130,19 @@ track(
|
|
|
130
130
|
);
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
+
`source` is either an action's `ctx`, passed straight through, or a
|
|
134
|
+
`{ userId, anonymousId, sessionId }` meta object for callers that have no `ctx`.
|
|
135
|
+
|
|
136
|
+
The browser session is not something the caller has to supply: it comes from the
|
|
137
|
+
ambient request context (`RequestContext.browserSessionId`, set from the
|
|
138
|
+
`X-Agent-Native-Session-Id` header the action client, chat, and client `track()`
|
|
139
|
+
all send), so the same visit correlates whether the UI or the agent triggered the
|
|
140
|
+
event. Each provider maps it to its own session field — `$session_id` for
|
|
141
|
+
PostHog, which joins the event to session replay; a `session_id` property for
|
|
142
|
+
Mixpanel and Amplitude; a top-level `sessionId` for webhooks and Agent Native
|
|
143
|
+
Analytics. It is absent for callers with no browser (cron, CLI, MCP, A2A), and a
|
|
144
|
+
`sessionId` in the meta object overrides it.
|
|
145
|
+
|
|
133
146
|
### `identify(userId, traits?)` {#identify}
|
|
134
147
|
|
|
135
148
|
Identify a user with traits. Forwarded to providers that support it (PostHog, Mixpanel, Amplitude, webhook).
|
|
@@ -166,7 +179,9 @@ export default defineAction({
|
|
|
166
179
|
.values({ name, template })
|
|
167
180
|
.returning();
|
|
168
181
|
|
|
169
|
-
|
|
182
|
+
// Pass ctx straight through: the event is attributed to ctx.userEmail and
|
|
183
|
+
// joins the browser session the call came from.
|
|
184
|
+
track("project.created", { name, template }, ctx);
|
|
170
185
|
|
|
171
186
|
return { ok: true, projectId: project.id };
|
|
172
187
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.144.
|
|
3
|
+
"version": "0.144.2",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -283,7 +283,11 @@ same best-effort fan-out as other tracking events.
|
|
|
283
283
|
upward through `$ai_parent_id` so a backend can rebuild the tree.
|
|
284
284
|
`$ai_session_id` is the thread; the browser session is separate and ships as
|
|
285
285
|
`$session_id`, read from `X-Agent-Native-Session-Id` via
|
|
286
|
-
`RequestContext.browserSessionId`.
|
|
286
|
+
`RequestContext.browserSessionId`. The agent chat and the action client both
|
|
287
|
+
send that header, so a UI action call and the agent's own call during one
|
|
288
|
+
visit share a session. `setAnalyticsSessionId()` from
|
|
289
|
+
`@agent-native/core/client/analytics` pins a custom id and opts it out of the
|
|
290
|
+
30-minute idle rotation. Emission lives in `posthog-ai.ts`.
|
|
287
291
|
- Agent Native Analytics shape: the same event lands in `analytics_events` with
|
|
288
292
|
mirrored query-friendly properties such as `run_id`, `thread_id`,
|
|
289
293
|
`cost_cents_x100`, `duration_ms`, `tool_calls`, `successful_tools`,
|
|
@@ -18,19 +18,26 @@ The tracking system provides a single `track()` call that fans out to all regist
|
|
|
18
18
|
## How It Works
|
|
19
19
|
|
|
20
20
|
1. At server startup, `registerBuiltinProviders()` checks env vars and registers any configured providers.
|
|
21
|
-
2. Application code calls `track(eventName, properties,
|
|
21
|
+
2. Application code calls `track(eventName, properties, source)` from actions, plugins, or server routes.
|
|
22
22
|
3. The registry fans out the event to every registered provider. Errors are caught and logged -- a failing provider never crashes the caller.
|
|
23
23
|
4. Built-in providers batch HTTP calls (flush every 10 seconds or 50 events, whichever comes first).
|
|
24
24
|
|
|
25
25
|
## API
|
|
26
26
|
|
|
27
|
-
### `track(name, properties?,
|
|
27
|
+
### `track(name, properties?, source?)`
|
|
28
28
|
|
|
29
|
-
Fire an analytics event.
|
|
29
|
+
Fire an analytics event. `source` is either a `{ userId, anonymousId, sessionId }`
|
|
30
|
+
meta object or an action's `ctx` passed straight through.
|
|
30
31
|
|
|
31
32
|
```ts
|
|
32
33
|
import { track } from "@agent-native/core/tracking";
|
|
33
34
|
|
|
35
|
+
// From an action — pass ctx; userId comes from ctx.userEmail.
|
|
36
|
+
run: async ({ name }, ctx) => {
|
|
37
|
+
track("meal.logged", { mealName: name, calories: 350 }, ctx);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// From a plugin or route with no ctx.
|
|
34
41
|
track(
|
|
35
42
|
"meal.logged",
|
|
36
43
|
{ mealName: "Salad", calories: 350 },
|
|
@@ -38,6 +45,16 @@ track(
|
|
|
38
45
|
);
|
|
39
46
|
```
|
|
40
47
|
|
|
48
|
+
The caller's browser session comes from the ambient request context
|
|
49
|
+
(`RequestContext.browserSessionId`, set from the `X-Agent-Native-Session-Id`
|
|
50
|
+
header), so it resolves the same whether the UI called the action or the agent
|
|
51
|
+
did. Pass `sessionId` in the meta object to override it — routes that run
|
|
52
|
+
outside a request context, such as `/_agent-native/track`, do exactly that.
|
|
53
|
+
Providers map it to their own session field: `$session_id` for PostHog (which
|
|
54
|
+
joins the event to session replay), `session_id` as a property for Mixpanel and
|
|
55
|
+
Amplitude, a top-level `sessionId` for webhooks and Agent Native Analytics. It
|
|
56
|
+
is absent for callers with no browser — cron, CLI, MCP, A2A.
|
|
57
|
+
|
|
41
58
|
### `identify(userId, traits?)`
|
|
42
59
|
|
|
43
60
|
Identify a user with traits. Forwarded to providers that support it.
|