@agent-native/core 0.109.0 → 0.109.1

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.
Files changed (125) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +7 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/a2a/client.ts +24 -2
  5. package/corpus/core/src/a2a/correlation.ts +50 -0
  6. package/corpus/core/src/a2a/handlers.ts +140 -14
  7. package/corpus/core/src/a2a/index.ts +1 -0
  8. package/corpus/core/src/a2a/invoke.ts +10 -1
  9. package/corpus/core/src/a2a/task-store.ts +146 -6
  10. package/corpus/core/src/a2a/types.ts +16 -0
  11. package/corpus/core/src/action.ts +2 -0
  12. package/corpus/core/src/agent/production-agent.ts +23 -0
  13. package/corpus/core/src/client/use-run-stuck-detection.ts +27 -1
  14. package/corpus/core/src/db/client.ts +55 -0
  15. package/corpus/core/src/observability/traces.ts +172 -19
  16. package/corpus/core/src/scripts/call-agent.ts +53 -2
  17. package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +72 -25
  18. package/corpus/core/src/server/agent-chat-plugin.ts +107 -6
  19. package/corpus/core/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +21 -0
  20. package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +7 -1
  21. package/corpus/core/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -1
  22. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +1 -1
  23. package/corpus/templates/analytics/changelog/2026-07-17-analytics-now-includes-a-default-alert-for-spikes-in-stuck-agent-chats.md +6 -0
  24. package/corpus/templates/analytics/docs/schemas/first-party-analytics.md +23 -14
  25. package/corpus/templates/analytics/docs/schemas/tracked-events.md +5 -1
  26. package/corpus/templates/analytics/server/jobs/analytics-alerts.ts +3 -3
  27. package/corpus/templates/analytics/server/lib/analytics-alerts.ts +209 -87
  28. package/corpus/templates/clips/actions/finalize-recording.ts +541 -62
  29. package/corpus/templates/clips/app/components/player/playback-comment-overlay.tsx +17 -2
  30. package/corpus/templates/clips/app/components/player/video-player.tsx +14 -2
  31. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +2 -2
  32. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +1 -1
  33. package/corpus/templates/clips/app/routes/record.tsx +3 -3
  34. package/corpus/templates/clips/app/routes/share.$shareId.tsx +6 -3
  35. package/corpus/templates/clips/changelog/2026-07-17-clips-now-retries-storage-verification-in-the-background-ins.md +6 -0
  36. package/corpus/templates/clips/changelog/2026-07-17-fixed-broken-thumbnail-icons-appearing-over-playable-clip-em.md +6 -0
  37. package/corpus/templates/clips/changelog/2026-07-17-playback-comments-stay-readable-at-faster-playback-speeds.md +6 -0
  38. package/corpus/templates/clips/chrome-extension/src/finalize-recovery.ts +3 -0
  39. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +3 -3
  40. package/corpus/templates/clips/desktop/src/app.tsx +18 -6
  41. package/corpus/templates/clips/desktop/src/lib/recorder.ts +111 -13
  42. package/corpus/templates/clips/desktop/src/lib/upload-verification.ts +16 -7
  43. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +2 -4
  44. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/live_upload.rs +32 -15
  45. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +45 -18
  46. package/corpus/templates/clips/server/jobs/media-verification.ts +130 -0
  47. package/corpus/templates/clips/server/lib/builder-media-compression.ts +15 -0
  48. package/corpus/templates/clips/server/lib/media-verification-state.ts +38 -0
  49. package/corpus/templates/clips/server/lib/player-thumbnail-url.ts +31 -0
  50. package/corpus/templates/clips/server/lib/post-finalize-dispatch.ts +10 -3
  51. package/corpus/templates/clips/server/plugins/auth.ts +5 -5
  52. package/corpus/templates/clips/server/plugins/calendar-jobs.ts +2 -0
  53. package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +12 -2
  54. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +6 -1
  55. package/corpus/templates/clips/server/routes/api/thumbnail/[recordingId].get.ts +340 -0
  56. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +24 -7
  57. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +91 -7
  58. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/status.get.ts +14 -0
  59. package/corpus/templates/clips/shared/finalize-recovery.ts +61 -2
  60. package/corpus/templates/clips/shared/share-meta.ts +1 -1
  61. package/dist/a2a/client.d.ts +11 -2
  62. package/dist/a2a/client.d.ts.map +1 -1
  63. package/dist/a2a/client.js +16 -3
  64. package/dist/a2a/client.js.map +1 -1
  65. package/dist/a2a/correlation.d.ts +10 -0
  66. package/dist/a2a/correlation.d.ts.map +1 -0
  67. package/dist/a2a/correlation.js +40 -0
  68. package/dist/a2a/correlation.js.map +1 -0
  69. package/dist/a2a/handlers.d.ts.map +1 -1
  70. package/dist/a2a/handlers.js +100 -15
  71. package/dist/a2a/handlers.js.map +1 -1
  72. package/dist/a2a/index.d.ts +1 -1
  73. package/dist/a2a/index.d.ts.map +1 -1
  74. package/dist/a2a/index.js.map +1 -1
  75. package/dist/a2a/invoke.d.ts +4 -1
  76. package/dist/a2a/invoke.d.ts.map +1 -1
  77. package/dist/a2a/invoke.js +3 -0
  78. package/dist/a2a/invoke.js.map +1 -1
  79. package/dist/a2a/task-store.d.ts +12 -1
  80. package/dist/a2a/task-store.d.ts.map +1 -1
  81. package/dist/a2a/task-store.js +96 -6
  82. package/dist/a2a/task-store.js.map +1 -1
  83. package/dist/a2a/types.d.ts +15 -0
  84. package/dist/a2a/types.d.ts.map +1 -1
  85. package/dist/a2a/types.js.map +1 -1
  86. package/dist/action.d.ts +2 -0
  87. package/dist/action.d.ts.map +1 -1
  88. package/dist/action.js.map +1 -1
  89. package/dist/agent/production-agent.d.ts +9 -0
  90. package/dist/agent/production-agent.d.ts.map +1 -1
  91. package/dist/agent/production-agent.js +14 -0
  92. package/dist/agent/production-agent.js.map +1 -1
  93. package/dist/client/use-run-stuck-detection.d.ts.map +1 -1
  94. package/dist/client/use-run-stuck-detection.js +25 -1
  95. package/dist/client/use-run-stuck-detection.js.map +1 -1
  96. package/dist/collab/routes.d.ts +1 -1
  97. package/dist/collab/struct-routes.d.ts +1 -1
  98. package/dist/db/client.d.ts +7 -0
  99. package/dist/db/client.d.ts.map +1 -1
  100. package/dist/db/client.js +33 -0
  101. package/dist/db/client.js.map +1 -1
  102. package/dist/observability/routes.d.ts +5 -5
  103. package/dist/observability/traces.d.ts +9 -0
  104. package/dist/observability/traces.d.ts.map +1 -1
  105. package/dist/observability/traces.js +137 -19
  106. package/dist/observability/traces.js.map +1 -1
  107. package/dist/resources/handlers.d.ts +2 -2
  108. package/dist/scripts/call-agent.d.ts.map +1 -1
  109. package/dist/scripts/call-agent.js +35 -3
  110. package/dist/scripts/call-agent.js.map +1 -1
  111. package/dist/secrets/routes.d.ts +9 -9
  112. package/dist/server/agent-chat/action-filters-a2a.d.ts +18 -2
  113. package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
  114. package/dist/server/agent-chat/action-filters-a2a.js +35 -7
  115. package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
  116. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  117. package/dist/server/agent-chat-plugin.js +93 -6
  118. package/dist/server/agent-chat-plugin.js.map +1 -1
  119. package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +21 -0
  120. package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +7 -1
  121. package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -1
  122. package/package.json +1 -1
  123. package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +21 -0
  124. package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +7 -1
  125. package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -1
@@ -23,6 +23,10 @@ import {
23
23
  isLoopbackAddress,
24
24
  isTrustedLocalRuntime,
25
25
  } from "../a2a/auth-policy.js";
26
+ import {
27
+ sanitizeA2ACorrelationId,
28
+ sanitizeA2ACorrelationMetadata,
29
+ } from "../a2a/correlation.js";
26
30
  import { applyAgentTextEventToBuffer } from "../a2a/response-text.js";
27
31
  import {
28
32
  createA2AApproval,
@@ -115,6 +119,7 @@ import {
115
119
  type ForkThreadSourceSnapshot,
116
120
  } from "../chat-threads/store.js";
117
121
  import { createDbAdminAgentTools } from "../db-admin/agent-tools.js";
122
+ import { isTransientDatabaseError } from "../db/client.js";
118
123
  import {
119
124
  verifyInternalToken,
120
125
  extractBearerToken,
@@ -185,6 +190,55 @@ import {
185
190
  export { handleSharedThreadRequest };
186
191
  export type { SharedThreadRouteDependencies };
187
192
 
193
+ function withTransientDatabaseFallback(
194
+ route: string,
195
+ handler: (event: H3Event) => unknown,
196
+ ) {
197
+ return defineEventHandler(async (event) => {
198
+ try {
199
+ return await handler(event);
200
+ } catch (error) {
201
+ if (!isTransientDatabaseError(error)) throw error;
202
+
203
+ const method = getMethod(event);
204
+ const retrySafe = method === "GET" || method === "HEAD";
205
+
206
+ const databaseError = error as {
207
+ code?: unknown;
208
+ cause?: { code?: unknown };
209
+ };
210
+ captureError(new Error("Transient database failure in agent chat"), {
211
+ route,
212
+ method,
213
+ tags: {
214
+ source: "agent-chat",
215
+ failureClass: "transient-database",
216
+ },
217
+ extra: {
218
+ databaseErrorName: error instanceof Error ? error.name : typeof error,
219
+ databaseErrorCode:
220
+ typeof databaseError.code === "string"
221
+ ? databaseError.code
222
+ : undefined,
223
+ databaseCauseCode:
224
+ typeof databaseError.cause?.code === "string"
225
+ ? databaseError.cause.code
226
+ : undefined,
227
+ },
228
+ });
229
+ setResponseStatus(event, 503);
230
+ if (retrySafe) setResponseHeader(event, "Retry-After", "2");
231
+ return {
232
+ error: retrySafe
233
+ ? "The database is temporarily unavailable. Please retry."
234
+ : "The database became unavailable while processing this request. Refresh before deciding whether to retry.",
235
+ code: "database_unavailable",
236
+ retryable: retrySafe,
237
+ };
238
+ }
239
+ });
240
+ }
241
+
188
242
  // Lazy fs — loaded via dynamic import() on first use.
189
243
  // This avoids require() which bundlers convert to createRequire(import.meta.url)
190
244
  // that crashes on CF Workers where import.meta.url is undefined.
@@ -1209,6 +1263,7 @@ export function createAgentChatPlugin(
1209
1263
 
1210
1264
  const { mountA2A } = await import("../a2a/server.js");
1211
1265
  mountA2A(nitroApp, {
1266
+ appId: options?.appId,
1212
1267
  name: options?.appId
1213
1268
  ? options.appId.charAt(0).toUpperCase() + options.appId.slice(1)
1214
1269
  : "Agent",
@@ -1217,7 +1272,7 @@ export function createAgentChatPlugin(
1217
1272
  publicSkillsOnly: true,
1218
1273
  streaming: true,
1219
1274
  durableBackgroundRuns: options?.durableBackgroundRuns,
1220
- executeReadOnlyAction: async ({ action, input }) => {
1275
+ executeReadOnlyAction: async ({ action, input, invocationId }) => {
1221
1276
  const actions = filterDirectA2AActions(
1222
1277
  mcpFullActions ?? allScripts,
1223
1278
  options ?? {},
@@ -1234,10 +1289,12 @@ export function createAgentChatPlugin(
1234
1289
  actions: { [action]: entry },
1235
1290
  name: action,
1236
1291
  input,
1237
- callId: `a2a-action-${crypto.randomUUID()}`,
1292
+ callId: `a2a-action-${invocationId}`,
1238
1293
  ownerEmail: getRequestUserEmail(),
1239
1294
  orgId: getRequestOrgId() ?? null,
1240
1295
  caller: "a2a",
1296
+ networkProtocol: "a2a",
1297
+ networkId: invocationId,
1241
1298
  });
1242
1299
  if (result.status === "approval_required") {
1243
1300
  return {
@@ -1574,6 +1631,11 @@ export function createAgentChatPlugin(
1574
1631
  const recoverableArtifactStatusWriter =
1575
1632
  createSerializedA2ATaskStatusWriter(context.taskId);
1576
1633
  const controller = new AbortController();
1634
+ const correlation = sanitizeA2ACorrelationMetadata(context.metadata);
1635
+ const telemetryThreadId =
1636
+ sanitizeA2ACorrelationId(context.contextId) ??
1637
+ correlation.callerThreadId ??
1638
+ context.taskId;
1577
1639
 
1578
1640
  console.log(
1579
1641
  `[A2A] Starting agent loop: ${a2aToolSurface.tools.length}/${a2aToolSurface.availableTools.length} initial tools, prompt ${systemPrompt.length} chars`,
@@ -1598,6 +1660,11 @@ export function createAgentChatPlugin(
1598
1660
  toolCallCacheKey(approved.tool, approved.input),
1599
1661
  ),
1600
1662
  executionMode: "act",
1663
+ runId: context.taskId,
1664
+ networkProtocol: "a2a",
1665
+ networkId: context.taskId,
1666
+ threadId: context.taskId,
1667
+ turnId: context.taskId,
1601
1668
  send: (event) => {
1602
1669
  a2aEvents.push(event);
1603
1670
  if (event.type === "tool_start") {
@@ -1658,6 +1725,26 @@ export function createAgentChatPlugin(
1658
1725
  options?.durableBackgroundRuns === true &&
1659
1726
  isInBackgroundFunctionRuntime(),
1660
1727
  },
1728
+ {
1729
+ telemetry: {
1730
+ runId: context.taskId,
1731
+ threadId: telemetryThreadId,
1732
+ userId: userEmail,
1733
+ delegation: {
1734
+ protocol: "a2a",
1735
+ taskId: context.taskId,
1736
+ ...(correlation.callerApp
1737
+ ? { callerApp: correlation.callerApp }
1738
+ : {}),
1739
+ ...(correlation.parentRunId
1740
+ ? { parentRunId: correlation.parentRunId }
1741
+ : {}),
1742
+ ...(correlation.parentTurnId
1743
+ ? { parentTurnId: correlation.parentTurnId }
1744
+ : {}),
1745
+ },
1746
+ },
1747
+ },
1661
1748
  );
1662
1749
 
1663
1750
  // The continuation can observe terminal output immediately, so make
@@ -1814,6 +1901,7 @@ export function createAgentChatPlugin(
1814
1901
  : {}),
1815
1902
  askAgent: async (message: string) => {
1816
1903
  const ownerEmail = getRequestUserEmail();
1904
+ const mcpRunId = crypto.randomUUID();
1817
1905
  const { getOwnerActiveApiKey } =
1818
1906
  await import("../agent/production-agent.js");
1819
1907
  const ownerApiKey = ownerEmail
@@ -1942,9 +2030,14 @@ export function createAgentChatPlugin(
1942
2030
  },
1943
2031
  ],
1944
2032
  actions: mcpActions,
1945
- ownerEmail: getRequestUserEmail(),
2033
+ ownerEmail,
1946
2034
  orgId: getRequestOrgId() ?? null,
1947
2035
  executionMode: "act",
2036
+ runId: mcpRunId,
2037
+ networkProtocol: "mcp",
2038
+ networkId: mcpRunId,
2039
+ threadId: mcpRunId,
2040
+ turnId: mcpRunId,
1948
2041
  send: (event) => {
1949
2042
  accumulatedText = applyAgentTextEventToBuffer(
1950
2043
  accumulatedText,
@@ -1962,6 +2055,14 @@ export function createAgentChatPlugin(
1962
2055
  options?.durableBackgroundRuns === true &&
1963
2056
  isInBackgroundFunctionRuntime(),
1964
2057
  },
2058
+ {
2059
+ telemetry: {
2060
+ runId: mcpRunId,
2061
+ threadId: mcpRunId,
2062
+ userId: ownerEmail ?? null,
2063
+ delegation: { protocol: "mcp" },
2064
+ },
2065
+ },
1965
2066
  );
1966
2067
 
1967
2068
  return accumulatedText || "(no response)";
@@ -4234,7 +4335,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
4234
4335
  // GET /runs/active?threadId=X — check if there's an active run for a thread
4235
4336
  getH3App(nitroApp).use(
4236
4337
  `${routePath}/runs`,
4237
- defineEventHandler(async (event) => {
4338
+ withTransientDatabaseFallback(`${routePath}/runs`, async (event) => {
4238
4339
  // Auth check — ensure the user is authenticated
4239
4340
  const owner = await getOwnerFromEvent(event);
4240
4341
 
@@ -4753,7 +4854,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
4753
4854
  `${getOrigin(event)}${routePath}/shared/${encodeURIComponent(token)}`;
4754
4855
  getH3App(nitroApp).use(
4755
4856
  `${routePath}/threads`,
4756
- defineEventHandler(async (event) => {
4857
+ withTransientDatabaseFallback(`${routePath}/threads`, async (event) => {
4757
4858
  const owner = await getOwnerFromEvent(event);
4758
4859
  const orgId = await getOrgIdFromEvent(event);
4759
4860
  const method = getMethod(event);
@@ -5383,7 +5484,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
5383
5484
  // earlier-mounted handlers (mode, save-key, files, skills, mentions, threads) handle them.
5384
5485
  getH3App(nitroApp).use(
5385
5486
  routePath,
5386
- defineEventHandler(async (event) => {
5487
+ withTransientDatabaseFallback(routePath, async (event) => {
5387
5488
  // Skip sub-path requests — they're handled by earlier-mounted handlers
5388
5489
  const url = event.node?.req?.url || event.path || "";
5389
5490
  const afterBase = url.slice(
@@ -144,6 +144,27 @@ message delegation for planning, synthesis, multi-step work, or mutations.
144
144
  Inside an agent loop, `call-agent` exposes the same path with `action` + `input`;
145
145
  omit `message` and `taskId` in that mode.
146
146
 
147
+ ### Retry safety and trace linkage
148
+
149
+ `call-agent` automatically derives an owner-scoped idempotency key from the
150
+ originating turn, target, and exact message. If a retry reaches the receiver
151
+ after the caller timed out, asynchronous `message/send` returns the existing
152
+ active or completed task instead of starting a duplicate agent run. Failed and
153
+ canceled tasks release the key for an intentional retry; synchronous calls are
154
+ never deduplicated. Lower-level clients may pass an
155
+ `idempotencyKey` explicitly; keep it stable for the same logical submission and
156
+ change it when the work changes. Dedupe is scoped to the JWT-authenticated
157
+ owner and verified org, and keys are limited to 128 characters.
158
+
159
+ The caller also forwards bounded correlation metadata (`callerApp`,
160
+ `callerThreadId`, `parentRunId`, `parentTurnId`, and direct-read
161
+ `invocationId`). These fields
162
+ are telemetry hints only. Receivers must continue to derive identity,
163
+ ownership, org scope, access, and approval from the verified request context.
164
+ Delegated model loops emit `$ai_generation` with A2A/MCP lineage, while direct
165
+ reads emit the content-free `$a2a_read_invoke` event; neither event includes
166
+ action arguments or results.
167
+
147
168
  ### Advanced: `A2AClient` (full control)
148
169
 
149
170
  ```ts
@@ -285,7 +285,13 @@ LLM generation:
285
285
  - Agent Native Analytics shape: the same event lands in `analytics_events` with
286
286
  mirrored query-friendly properties such as `run_id`, `thread_id`,
287
287
  `cost_cents_x100`, `duration_ms`, `tool_calls`, `successful_tools`,
288
- `failed_tools`, and `status`.
288
+ `failed_tools`, and `status`. A content-free `tools` array includes at most
289
+ 50 tool names, start offsets, durations, statuses, and coarse error classes;
290
+ interrupted calls are finalized as errors, and failed runs still emit with
291
+ zero or known usage. `tools_truncated` marks longer runs while the rollup counts remain complete.
292
+ Delegated runs add `delegation_protocol`, `caller_app`, `a2a_task_id`, and
293
+ `parent_run_id` when available. `parent_turn_id` is separate because one
294
+ logical turn may span multiple concrete runs.
289
295
 
290
296
  Do not build a separate LLM-observability ingestion API unless there is a clear
291
297
  reason the tracking provider registry cannot express the use case. Keep prompt,
@@ -141,7 +141,7 @@ Other framework-level baseline events:
141
141
  - `signup` from Better Auth user creation, with `auth_provider`, `auth_user_id`, and first-touch referral attribution (`referral_source`, `referrer_user`, `referral_medium`, `referral_campaign`, `utm_*`, `first_touch_path`, `landing_referrer` — see "Referral / viral attribution" above)
142
142
  - `builder connect clicked` and `builder connect popup blocked` from browser Connect Builder CTAs
143
143
  - `builder connect started`, `builder connect succeeded`, `builder connect failed`, `builder disconnect succeeded`, and `builder disconnect failed` from the Builder connection routes, with LLM connection context when resolvable
144
- - `$ai_generation` from instrumented agent loops, with PostHog AI Observability fields such as `$ai_trace_id`, `$ai_session_id`, `$ai_model`, `$ai_provider`, `$ai_input_tokens`, `$ai_output_tokens`, `$ai_latency`, `$ai_total_cost_usd`, and mirrored Agent Native query fields such as `run_id`, `thread_id`, `cost_cents_x100`, `duration_ms`, `tool_calls`, and `status`. Prompt, tool argument, and output content is not included by default.
144
+ - `$ai_generation` from instrumented agent loops, with PostHog AI Observability fields such as `$ai_trace_id`, `$ai_session_id`, `$ai_model`, `$ai_provider`, `$ai_input_tokens`, `$ai_output_tokens`, `$ai_latency`, `$ai_total_cost_usd`, and mirrored Agent Native query fields such as `run_id`, `thread_id`, `cost_cents_x100`, `duration_ms`, `tool_calls`, and `status`. A bounded `tools` array contains names, start offsets, durations, statuses, and coarse error classes only; interrupted tools and failed runs remain visible, and delegated runs include protocol/task/parent-run/parent-turn correlation. Prompt, tool argument, result, and output content is excluded.
145
145
 
146
146
  For new lifecycle events, call `track()` server-side when the server is the source of truth, and `trackEvent()` client-side only for browser interactions.
147
147
 
@@ -15,7 +15,7 @@ function resolveScope() {
15
15
 
16
16
  export default defineAction({
17
17
  description:
18
- "Query the built-in first-party Analytics source: events recorded through this app's analytics collector endpoint (/track) and session replay summaries recorded through /api/analytics/replay. This source does not require an external provider connection. Use it for questions about app/site traffic, product events, template/app usage, conversions, session recordings, LLM/agent observability, model cost, token volume, latency, and other first-party data collected by this analytics app. Use source-specific actions such as BigQuery, GA4, Mixpanel, PostHog, or Amplitude when the user asks for those sources or the relevant data lives there. SQL may read analytics_events and session_recordings only; session_replay_chunks is intentionally unavailable, and reads are automatically scoped to the current user/org. analytics_events columns include event_name, timestamp, event_date, user_id, anonymous_id, user_key, session_id, app, template, signed_in, url, path, hostname, referrer, properties, and context. LLM observability events use event_name = '$ai_generation'; useful properties include $ai_trace_id/run_id, $ai_session_id/thread_id, $ai_model/model, $ai_provider/provider, $ai_input_tokens/input_tokens, $ai_output_tokens/output_tokens, cache_read_tokens, cache_write_tokens, cost_cents_x100, $ai_total_cost_usd/cost_usd, duration_ms/$ai_latency, status, tool_calls, successful_tools, failed_tools, and error_message/$ai_error. session_recordings columns include id, session_id, user_id, anonymous_id, user_key, started_at, ended_at, duration_ms, chunk_count, event_count, page_count, error_count, rage_click_count, app, template, status, first_url, last_url, path, hostname, referrer, and metadata.",
18
+ "Query the built-in first-party Analytics source: events recorded through this app's analytics collector endpoint (/track) and session replay summaries recorded through /api/analytics/replay. This source does not require an external provider connection. Use it for questions about app/site traffic, product events, template/app usage, conversions, session recordings, LLM/agent observability, model cost, token volume, latency, and other first-party data collected by this analytics app. Use source-specific actions such as BigQuery, GA4, Mixpanel, PostHog, or Amplitude when the user asks for those sources or the relevant data lives there. SQL may read analytics_events and session_recordings only; session_replay_chunks is intentionally unavailable, and reads are automatically scoped to the current user/org. analytics_events columns include event_name, timestamp, event_date, user_id, anonymous_id, user_key, session_id, app, template, signed_in, url, path, hostname, referrer, properties, and context. LLM observability events use event_name = '$ai_generation'; useful properties include $ai_trace_id/run_id, $ai_session_id/thread_id, $ai_model/model, $ai_provider/provider, $ai_input_tokens/input_tokens, $ai_output_tokens/output_tokens, cache_read_tokens, cache_write_tokens, cost_cents_x100, $ai_total_cost_usd/cost_usd, duration_ms/$ai_latency, status, tool_calls, successful_tools, failed_tools, tools, tools_truncated, delegated, delegation_protocol, caller_app, a2a_task_id, parent_run_id, parent_turn_id, and error_message/$ai_error. The bounded tools array contains names, relative start times, durations, statuses, and coarse error classes only, never args or results; failed runs and interrupted tools remain queryable. session_recordings columns include id, session_id, user_id, anonymous_id, user_key, started_at, ended_at, duration_ms, chunk_count, event_count, page_count, error_count, rage_click_count, app, template, status, first_url, last_url, path, hostname, referrer, and metadata.",
19
19
  schema: z.object({
20
20
  sql: z
21
21
  .string()
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-07-17
4
+ ---
5
+
6
+ Analytics now includes a default inbox alert for spikes in agent chats detected as stuck.
@@ -117,20 +117,29 @@ event_name = '$ai_sentiment'
117
117
 
118
118
  Useful query fields live in `properties`:
119
119
 
120
- | Property | Description |
121
- | ------------------------------------------------ | ----------------------------------------- |
122
- | `$ai_trace_id`, `run_id` | Agent run id |
123
- | `$ai_session_id`, `thread_id` | Chat/thread id, when available |
124
- | `$ai_model`, `model` | Model used |
125
- | `$ai_provider`, `provider` | Engine/provider name |
126
- | `$ai_input_tokens`, `input_tokens` | Input tokens |
127
- | `$ai_output_tokens`, `output_tokens` | Output tokens |
128
- | `cache_read_tokens`, `cache_write_tokens` | Prompt-cache token counts |
129
- | `$ai_total_cost_usd`, `cost_usd` | Estimated run cost in USD |
130
- | `cost_cents_x100` | Estimated run cost in centicents |
131
- | `$ai_latency`, `duration_ms` | Run duration in seconds / milliseconds |
132
- | `tool_calls`, `successful_tools`, `failed_tools` | Tool-call counts |
133
- | `$ai_is_error`, `status`, `$ai_error` | Error status and message, when applicable |
120
+ | Property | Description |
121
+ | ------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
122
+ | `$ai_trace_id`, `run_id` | Agent run id |
123
+ | `$ai_session_id`, `thread_id` | Chat/thread id, when available |
124
+ | `$ai_model`, `model` | Model used |
125
+ | `$ai_provider`, `provider` | Engine/provider name |
126
+ | `$ai_input_tokens`, `input_tokens` | Input tokens |
127
+ | `$ai_output_tokens`, `output_tokens` | Output tokens |
128
+ | `cache_read_tokens`, `cache_write_tokens` | Prompt-cache token counts |
129
+ | `$ai_total_cost_usd`, `cost_usd` | Estimated run cost in USD |
130
+ | `cost_cents_x100` | Estimated run cost in centicents |
131
+ | `$ai_latency`, `duration_ms` | Run duration in seconds / milliseconds |
132
+ | `tool_calls`, `successful_tools`, `failed_tools` | Complete tool-call counts |
133
+ | `tools`, `tools_truncated` | First 50 tool names, offsets, durations, statuses, and error classes, including interrupted calls |
134
+ | `delegated`, `delegation_protocol`, `caller_app` | Delegated-run attribution |
135
+ | `a2a_task_id`, `parent_run_id`, `parent_turn_id` | Cross-app trace linkage, when available |
136
+ | `$ai_is_error`, `status`, `$ai_error` | Error status and message, when applicable |
137
+
138
+ The `tools` array never includes tool arguments, results, or error messages.
139
+ Use `tools_truncated` with the complete `tool_calls` count when a run exceeds
140
+ the 50-entry detail cap.
141
+ Failed runs still emit a generation row with zero or known usage so delegated
142
+ timeouts and setup failures remain visible.
134
143
 
135
144
  Explicit thumbs feedback is content-free and uses these `$ai_feedback`
136
145
  properties:
@@ -29,7 +29,11 @@ Events tracked by application instrumentation and stored in the configured appli
29
29
  `$ai_model`, `$ai_provider`, `$ai_input_tokens`, `$ai_output_tokens`,
30
30
  `$ai_latency`, `$ai_total_cost_usd`, `run_id`, `thread_id`,
31
31
  `cost_cents_x100`, `duration_ms`, `tool_calls`, `status`, and error fields.
32
- Prompt and response content are excluded by default.
32
+ A bounded `tools` array records only tool names, relative start times,
33
+ durations, statuses, and coarse error classes; interrupted tools and failed
34
+ runs remain visible, and `tools_truncated` marks runs above the 50-entry cap.
35
+ Delegated runs also include protocol, caller, task, parent-run, and
36
+ parent-turn linkage. Prompt, argument, result, and response content are excluded.
33
37
 
34
38
  ### Content Editing
35
39
 
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  claimAnalyticsAlertRuleEvaluation,
3
- ensureDefaultHttp5xxSpikeAlertRules,
3
+ ensureDefaultAnalyticsAlertRules,
4
4
  evaluateAndNotifyAnalyticsAlertRule,
5
5
  listEnabledAnalyticsAlertRules,
6
6
  markAnalyticsAlertRuleError,
@@ -45,8 +45,8 @@ export async function runAnalyticsAlertsOnce(
45
45
 
46
46
  try {
47
47
  const sweepLimit = maxRulesPerSweep(options.limit);
48
- await ensureDefaultHttp5xxSpikeAlertRules().catch((err) => {
49
- console.error("[analytics-alerts] Default 5xx alert seed failed:", err);
48
+ await ensureDefaultAnalyticsAlertRules().catch((err) => {
49
+ console.error("[analytics-alerts] Default alert seed failed:", err);
50
50
  });
51
51
  let rules: Awaited<ReturnType<typeof listEnabledAnalyticsAlertRules>>;
52
52
  try {