@agent-native/core 0.144.1 → 0.144.3
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/templates/analytics/app/pages/DataSources.tsx +28 -4
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +7 -3
- package/corpus/templates/analytics/server/plugins/dashboard-report-jobs.ts +13 -3
- package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +64 -23
- package/corpus/templates/calendar/app/hooks/use-google-auth.ts +69 -46
- package/corpus/templates/calendar/app/hooks/use-view-preferences.ts +35 -6
- package/corpus/templates/calendar/app/hooks/use-zoom-auth.ts +24 -3
- package/corpus/templates/clips/app/components/player/transcript-panel.tsx +45 -6
- package/corpus/templates/clips/app/components/recorder/storage-setup-card.tsx +38 -5
- package/corpus/templates/clips/desktop/src/app.tsx +146 -45
- package/corpus/templates/clips/server/jobs/brain-export.ts +13 -6
- package/corpus/templates/clips/server/jobs/builder-media-compression.ts +10 -3
- package/corpus/templates/clips/server/jobs/meeting-reminders.ts +10 -3
- package/corpus/templates/clips/server/jobs/poll-calendars.ts +23 -21
- package/corpus/templates/clips/server/jobs/stale-meeting-sweeper.ts +10 -3
- package/corpus/templates/clips/server/jobs/transactional-emails.ts +10 -3
- package/corpus/templates/content/app/components/sidebar/NotionButton.tsx +1 -0
- package/corpus/templates/design/app/components/design/FusionAppBanner.tsx +15 -2
- package/corpus/templates/mail/app/components/GoogleConnectBanner.tsx +82 -39
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +31 -9
- package/corpus/templates/mail/server/plugins/mail-jobs.ts +36 -17
- package/dist/client/AssistantChat.js +73 -43
- package/dist/client/MultiTabAssistantChat.d.ts +3 -1
- package/dist/client/MultiTabAssistantChat.js +79 -92
- 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/browser-session-bridge.js +97 -54
- package/dist/client/db-admin/useAgentSync.js +34 -43
- package/dist/client/dynamic-suggestions.d.ts +3 -0
- package/dist/client/dynamic-suggestions.js +54 -52
- package/dist/client/hooks/index.d.ts +1 -0
- package/dist/client/hooks/index.js +1 -0
- package/dist/client/i18n.d.ts +7 -0
- package/dist/client/i18n.js +12 -3
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/integrations/useIntegrationStatus.d.ts +1 -1
- package/dist/client/integrations/useIntegrationStatus.js +9 -22
- package/dist/client/notifications/NotificationsBell.js +14 -9
- package/dist/client/progress/RunsTray.js +6 -5
- package/dist/client/settings/useBuilderStatus.js +95 -73
- package/dist/client/track.js +7 -0
- package/dist/client/transcription/BuilderTranscriptionCta.js +27 -31
- package/dist/client/use-action.js +10 -1
- package/dist/client/use-agent-engine-configured.js +21 -3
- package/dist/client/use-pausing-interval.d.ts +6 -0
- package/dist/client/use-pausing-interval.js +6 -0
- package/dist/client/use-poll-loop.d.ts +39 -0
- package/dist/client/use-poll-loop.js +73 -0
- package/dist/client/use-run-stuck-detection.js +41 -13
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/integrations/google-docs-poller.js +26 -7
- package/dist/integrations/pending-tasks-retry-job.js +17 -23
- package/dist/integrations/plugin.js +16 -15
- package/dist/integrations/remote-push-delivery-job.js +16 -25
- package/dist/integrations/remote-retry-job.js +19 -23
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/oauth-tokens/google-refresh.js +12 -19
- 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 +7 -7
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- 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-chat-plugin.js +26 -29
- 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/index.d.ts +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/interval-job.d.ts +25 -0
- package/dist/server/interval-job.js +19 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.js +1 -0
- package/dist/shared/poll-engine.d.ts +49 -0
- package/dist/shared/poll-engine.js +148 -0
- package/dist/templates/default/.agents/skills/internationalization/SKILL.md +5 -0
- package/dist/templates/workspace-core/.agents/skills/internationalization/SKILL.md +5 -0
- 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 +22 -2
- 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 +4 -3
- package/src/templates/default/.agents/skills/internationalization/SKILL.md +5 -0
- package/src/templates/workspace-core/.agents/skills/internationalization/SKILL.md +5 -0
- package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +5 -1
- package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +20 -3
|
@@ -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";
|
|
@@ -38,7 +38,16 @@ function setTimer(t) {
|
|
|
38
38
|
function enqueue(url, body, headers, options) {
|
|
39
39
|
const queue = getQueue();
|
|
40
40
|
queue.push({ url, body, headers });
|
|
41
|
-
|
|
41
|
+
// The batch timer is unref'd so it never keeps a long-lived server alive —
|
|
42
|
+
// but on Netlify/Vercel/Lambda the execution environment can freeze the
|
|
43
|
+
// moment the event loop looks empty, which is exactly when an unref'd timer
|
|
44
|
+
// doesn't count. Without a signal, a warm container freezing between
|
|
45
|
+
// invocations never fires that timer, silently dropping every provider's
|
|
46
|
+
// queued events for a request that ends in a crash. Default to flushing
|
|
47
|
+
// synchronously with the response in that environment; a caller (e.g.
|
|
48
|
+
// Agent Native Analytics' flush-mode override) can still force either way.
|
|
49
|
+
const flushImmediately = options?.flushImmediately ?? isServerlessRuntime();
|
|
50
|
+
if (flushImmediately || queue.length >= MAX_BATCH_SIZE) {
|
|
42
51
|
void drainQueue();
|
|
43
52
|
}
|
|
44
53
|
else if (!getTimer()) {
|
|
@@ -134,6 +143,7 @@ function createPostHogProvider(apiKey, host, errorTracking) {
|
|
|
134
143
|
properties: {
|
|
135
144
|
distinct_id: distinctId,
|
|
136
145
|
...properties,
|
|
146
|
+
...(event.sessionId ? { $session_id: event.sessionId } : {}),
|
|
137
147
|
timestamp: event.timestamp,
|
|
138
148
|
},
|
|
139
149
|
}));
|
|
@@ -168,6 +178,7 @@ function createPostHogProvider(apiKey, host, errorTracking) {
|
|
|
168
178
|
distinct_id: distinctId,
|
|
169
179
|
properties: {
|
|
170
180
|
...event.properties,
|
|
181
|
+
...(event.sessionId ? { $session_id: event.sessionId } : {}),
|
|
171
182
|
timestamp: event.timestamp,
|
|
172
183
|
},
|
|
173
184
|
}));
|
|
@@ -225,6 +236,9 @@ function createMixpanelProvider(token) {
|
|
|
225
236
|
? new Date(event.timestamp).getTime() / 1000
|
|
226
237
|
: undefined,
|
|
227
238
|
...event.properties,
|
|
239
|
+
// Mixpanel's own `$session_id` is numeric and assigned by its SDK, so
|
|
240
|
+
// the browser session lands as a plain property here.
|
|
241
|
+
...(event.sessionId ? { session_id: event.sessionId } : {}),
|
|
228
242
|
},
|
|
229
243
|
};
|
|
230
244
|
enqueue("https://api.mixpanel.com/track", JSON.stringify([data]));
|
|
@@ -253,7 +267,11 @@ function createAmplitudeProvider(apiKey) {
|
|
|
253
267
|
{
|
|
254
268
|
event_type: event.name,
|
|
255
269
|
user_id: event.userId || "anonymous",
|
|
256
|
-
|
|
270
|
+
// Amplitude's top-level `session_id` must be a numeric epoch, so
|
|
271
|
+
// the browser session ships as an event property instead.
|
|
272
|
+
event_properties: event.sessionId
|
|
273
|
+
? { ...event.properties, session_id: event.sessionId }
|
|
274
|
+
: event.properties,
|
|
257
275
|
time: event.timestamp
|
|
258
276
|
? new Date(event.timestamp).getTime()
|
|
259
277
|
: undefined,
|
|
@@ -290,6 +308,7 @@ function createWebhookProvider(url, authHeader) {
|
|
|
290
308
|
event: event.name,
|
|
291
309
|
properties: event.properties,
|
|
292
310
|
userId: event.userId,
|
|
311
|
+
sessionId: event.sessionId,
|
|
293
312
|
timestamp: event.timestamp,
|
|
294
313
|
}), extra);
|
|
295
314
|
},
|
|
@@ -318,6 +337,7 @@ function createAgentNativeAnalyticsProvider(publicKey, endpoint) {
|
|
|
318
337
|
properties: event.properties ?? {},
|
|
319
338
|
userId: event.userId,
|
|
320
339
|
anonymousId: event.anonymousId,
|
|
340
|
+
sessionId: event.sessionId,
|
|
321
341
|
timestamp: event.timestamp,
|
|
322
342
|
}), undefined, { flushImmediately });
|
|
323
343
|
},
|
|
@@ -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.3",
|
|
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": {
|
|
@@ -72,6 +72,7 @@
|
|
|
72
72
|
"./server/request-context": "./dist/server/request-context.js",
|
|
73
73
|
"./server/design-token-utils": "./dist/server/design-token-utils.js",
|
|
74
74
|
"./server/poll": "./dist/server/poll.js",
|
|
75
|
+
"./server/interval-job": "./dist/server/interval-job.js",
|
|
75
76
|
"./server/sse": "./dist/server/sse.js",
|
|
76
77
|
"./server/short-lived-token": "./dist/server/short-lived-token.js",
|
|
77
78
|
"./integrations": "./dist/integrations/index.js",
|
|
@@ -411,8 +412,8 @@
|
|
|
411
412
|
"y-protocols": "^1.0.7",
|
|
412
413
|
"yjs": "^13.6.31",
|
|
413
414
|
"zod": "^4.3.6",
|
|
414
|
-
"@agent-native/
|
|
415
|
-
"@agent-native/
|
|
415
|
+
"@agent-native/toolkit": "^0.13.3",
|
|
416
|
+
"@agent-native/recap-cli": "0.5.3"
|
|
416
417
|
},
|
|
417
418
|
"devDependencies": {
|
|
418
419
|
"@ai-sdk/anthropic": "^3.0.71",
|
|
@@ -29,6 +29,11 @@ Templates use `app/i18n/`:
|
|
|
29
29
|
Use BCP-47 filenames from the supported set: `en-US`, `zh-CN`, `es-ES`,
|
|
30
30
|
`fr-FR`, `de-DE`, `ja-JP`, `ko-KR`, `pt-BR`, `hi-IN`, `ar-SA`.
|
|
31
31
|
|
|
32
|
+
If an app only ships translations for a subset of that set, set
|
|
33
|
+
`supportedLocales` on the `AgentNativeI18nCatalog` to that subset. Without it,
|
|
34
|
+
`<LanguagePicker />` lists every framework-supported locale regardless of
|
|
35
|
+
whether `loadMessages` can actually resolve it.
|
|
36
|
+
|
|
32
37
|
## UI Pattern
|
|
33
38
|
|
|
34
39
|
- Wrap apps with `AppProviders i18n={{ catalog: i18nCatalog }}`.
|
|
@@ -29,6 +29,11 @@ Templates use `app/i18n/`:
|
|
|
29
29
|
Use BCP-47 filenames from the supported set: `en-US`, `zh-CN`, `es-ES`,
|
|
30
30
|
`fr-FR`, `de-DE`, `ja-JP`, `ko-KR`, `pt-BR`, `hi-IN`, `ar-SA`.
|
|
31
31
|
|
|
32
|
+
If an app only ships translations for a subset of that set, set
|
|
33
|
+
`supportedLocales` on the `AgentNativeI18nCatalog` to that subset. Without it,
|
|
34
|
+
`<LanguagePicker />` lists every framework-supported locale regardless of
|
|
35
|
+
whether `loadMessages` can actually resolve it.
|
|
36
|
+
|
|
32
37
|
## UI Pattern
|
|
33
38
|
|
|
34
39
|
- Wrap apps with `AppProviders i18n={{ catalog: i18nCatalog }}`.
|
|
@@ -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.
|