@agent-native/core 0.144.0 → 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/corpus/README.md +1 -1
- package/corpus/templates/analytics/actions/migrate-first-party-analytics-to-bigquery.ts +1 -1
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +36 -14
- package/corpus/templates/clips/app/components/player/player-controls.tsx +0 -3
- package/corpus/templates/clips/app/components/player/scrubber.tsx +2 -33
- package/corpus/templates/clips/app/components/player/video-player.tsx +58 -11
- package/corpus/templates/clips/changelog/2026-08-06-trimmed-clips-play-on-a-clean-timeline-without-cut-markers.md +6 -0
- package/corpus/templates/dispatch/AGENTS.md +8 -1
- package/corpus/templates/dispatch/app/dispatch-extensions.tsx +1 -0
- package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +30 -15
- package/corpus/templates/dispatch/app/i18n-data.ts +131 -0
- package/corpus/templates/dispatch/app/routes/admin._index.tsx +4 -0
- package/corpus/templates/dispatch/app/routes/admin.agents.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.approvals.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.apps.$appId.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.apps.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.audit.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.automations.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.destinations.tsx +4 -0
- package/corpus/templates/dispatch/app/routes/admin.dreams.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.identities.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.integrations.tsx +4 -0
- package/corpus/templates/dispatch/app/routes/admin.messaging.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.metrics.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.new-app.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.operations.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.thread-debug.tsx +4 -0
- package/corpus/templates/dispatch/app/routes/admin.transactional-email.$appId.$id.tsx +4 -0
- package/corpus/templates/dispatch/app/routes/admin.transactional-email.tsx +4 -0
- package/corpus/templates/dispatch/app/routes/admin.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.vault.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/admin.workspace.tsx +1 -0
- package/corpus/templates/dispatch/changelog/2026-08-06-dispatch-keeps-everyday-navigation-focused-while-management-.md +6 -0
- package/corpus/templates/slides/AGENTS.md +10 -12
- package/corpus/templates/slides/actions/export-pptx.ts +68 -0
- package/corpus/templates/slides/actions/get-deck.ts +12 -0
- package/corpus/templates/slides/actions/import-file.ts +130 -55
- package/corpus/templates/slides/actions/import-pptx.ts +108 -8
- package/corpus/templates/slides/actions/patch-deck.ts +51 -0
- package/corpus/templates/slides/actions/update-slide.ts +29 -3
- package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +11 -0
- package/corpus/templates/slides/app/lib/create-deck-generation.ts +72 -11
- package/corpus/templates/slides/app/lib/import-uploaded-deck.ts +89 -0
- package/corpus/templates/slides/app/pages/Index.tsx +85 -18
- package/corpus/templates/slides/changelog/2026-08-06-uploaded-decks-keep-their-slides-and-visuals-when-you-improv.md +6 -0
- package/corpus/templates/slides/server/handlers/import/html-converter.ts +13 -2
- package/corpus/templates/slides/server/lib/chat-attachments.ts +2 -2
- package/corpus/templates/slides/server/lib/source-import.ts +167 -0
- package/corpus/templates/slides/server/plugins/agent-chat.ts +8 -0
- 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/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/jobs/scheduler.js +79 -23
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/notifications/routes.d.ts +3 -3
- 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 +9 -9
- 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/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/creating-templates.mdx +1 -1
- package/docs/content/getting-started-actions.mdx +38 -189
- package/docs/content/getting-started-database.mdx +80 -164
- package/docs/content/getting-started-pages.mdx +44 -155
- package/docs/content/getting-started.mdx +26 -106
- 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
|
@@ -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
30
|
error?: undefined;
|
|
32
31
|
ok: boolean;
|
|
32
|
+
count?: undefined;
|
|
33
33
|
}>>;
|
|
@@ -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,37 +34,37 @@ 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
|
-
error?: undefined;
|
|
41
40
|
ok: boolean;
|
|
42
41
|
status: string;
|
|
42
|
+
error?: undefined;
|
|
43
43
|
} | {
|
|
44
|
-
ok?: undefined;
|
|
45
44
|
error: string;
|
|
46
45
|
removed?: undefined;
|
|
46
|
+
ok?: undefined;
|
|
47
47
|
} | {
|
|
48
|
-
error?: undefined;
|
|
49
48
|
ok: boolean;
|
|
50
49
|
removed: boolean;
|
|
50
|
+
error?: undefined;
|
|
51
51
|
}>>;
|
|
52
52
|
/**
|
|
53
53
|
* POST /_agent-native/secrets/:key/test — validate an optional candidate value
|
|
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
|
-
error?: undefined;
|
|
62
61
|
ok: boolean;
|
|
63
62
|
note?: undefined;
|
|
64
|
-
} | {
|
|
65
63
|
error?: undefined;
|
|
64
|
+
} | {
|
|
66
65
|
ok: boolean;
|
|
67
66
|
note: string;
|
|
67
|
+
error?: undefined;
|
|
68
68
|
} | {
|
|
69
69
|
note?: undefined;
|
|
70
70
|
ok: boolean;
|
|
@@ -95,11 +95,11 @@ export interface AdHocSecretPayload {
|
|
|
95
95
|
export declare function createAdHocSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<AdHocSecretPayload[] | {
|
|
96
96
|
error: string;
|
|
97
97
|
} | {
|
|
98
|
-
error?: undefined;
|
|
99
98
|
ok: boolean;
|
|
100
99
|
key: string;
|
|
101
|
-
} | {
|
|
102
100
|
error?: undefined;
|
|
101
|
+
} | {
|
|
103
102
|
ok: boolean;
|
|
104
103
|
removed: boolean;
|
|
104
|
+
error?: undefined;
|
|
105
105
|
}>>;
|
|
@@ -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 {
|
|
@@ -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;
|
|
@@ -49,7 +49,7 @@ npx @agent-native/core@latest create my-platform
|
|
|
49
49
|
|
|
50
50
|
Chat gives you auth, durable chat threads, SQL-backed resources, tools, application state, actions, and polling sync. You add the domain model and product UI.
|
|
51
51
|
|
|
52
|
-
If you are not building a reusable UI template yet, use
|
|
52
|
+
If you are not building a reusable UI template yet, use [Automation-First Apps](/docs/pure-agent-apps): define actions, run them without a browser, and add UI later when the workflow needs a durable surface.
|
|
53
53
|
|
|
54
54
|
## Project Structure {#project-structure}
|
|
55
55
|
|