@agent-native/core-corpus 0.201.0-nightly-20261005153421 → 0.201.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/security/SKILL.md +9 -2
  3. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +14 -4
  4. package/corpus/templates/analytics/.agents/skills/storing-data/SKILL.md +12 -7
  5. package/corpus/templates/analytics/CHANGELOG.md +6 -0
  6. package/corpus/templates/analytics/README.md +2 -2
  7. package/corpus/templates/analytics/changelog/2026-10-02-events-with-a-very-long-name-app-or-page-path-no-longer-caus.md +6 -0
  8. package/corpus/templates/analytics/package.json +1 -0
  9. package/corpus/templates/analytics/server/handlers/first-party-analytics.ts +9 -19
  10. package/corpus/templates/analytics/server/handlers/session-replay.ts +34 -47
  11. package/corpus/templates/analytics/server/lib/first-party-analytics-rollups.ts +9 -6
  12. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +57 -36
  13. package/corpus/templates/analytics/server/lib/indexed-text.ts +53 -0
  14. package/corpus/templates/analytics/server/lib/request-errors.ts +73 -0
  15. package/corpus/templates/analytics/server/lib/session-event-index.ts +6 -16
  16. package/corpus/templates/analytics/server/lib/session-replay.ts +12 -2
  17. package/corpus/templates/analytics/server/plugins/auth.ts +2 -1
  18. package/corpus/templates/analytics/server/plugins/otel.ts +6 -0
  19. package/corpus/templates/assets/.agents/skills/security/SKILL.md +9 -2
  20. package/corpus/templates/assets/.agents/skills/storing-data/SKILL.md +12 -7
  21. package/corpus/templates/assets/CHANGELOG.md +7 -0
  22. package/corpus/templates/assets/README.md +2 -2
  23. package/corpus/templates/assets/package.json +1 -0
  24. package/corpus/templates/assets/server/plugins/auth.ts +2 -1
  25. package/corpus/templates/assets/server/plugins/otel.ts +6 -0
  26. package/corpus/templates/brain/.agents/skills/brain/RUNBOOK.md +2 -1
  27. package/corpus/templates/brain/.agents/skills/brain/SKILL.md +5 -4
  28. package/corpus/templates/brain/.agents/skills/security/SKILL.md +9 -2
  29. package/corpus/templates/brain/.agents/skills/storing-data/SKILL.md +12 -7
  30. package/corpus/templates/brain/CHANGELOG.md +6 -0
  31. package/corpus/templates/brain/README.md +12 -8
  32. package/corpus/templates/brain/changelog/2026-10-05-private-slack-channels-are-searchable-org-wide.md +6 -0
  33. package/corpus/templates/brain/package.json +1 -0
  34. package/corpus/templates/brain/server/lib/audiences.ts +1 -98
  35. package/corpus/templates/brain/server/lib/connectors.ts +2 -166
  36. package/corpus/templates/brain/server/plugins/otel.ts +6 -0
  37. package/corpus/templates/calendar/.agents/skills/security/SKILL.md +9 -2
  38. package/corpus/templates/calendar/.agents/skills/storing-data/SKILL.md +12 -7
  39. package/corpus/templates/calendar/CHANGELOG.md +7 -0
  40. package/corpus/templates/calendar/README.md +2 -2
  41. package/corpus/templates/calendar/package.json +1 -0
  42. package/corpus/templates/calendar/server/plugins/auth.ts +2 -1
  43. package/corpus/templates/calendar/server/plugins/otel.ts +6 -0
  44. package/corpus/templates/chat/.agents/skills/security/SKILL.md +9 -2
  45. package/corpus/templates/chat/.agents/skills/storing-data/SKILL.md +12 -7
  46. package/corpus/templates/chat/README.md +2 -2
  47. package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +9 -0
  48. package/corpus/templates/clips/.agents/skills/security/SKILL.md +9 -2
  49. package/corpus/templates/clips/CHANGELOG.md +24 -0
  50. package/corpus/templates/clips/README.md +2 -2
  51. package/corpus/templates/clips/actions/lib/audio-chunk-list.ts +23 -0
  52. package/corpus/templates/clips/actions/lib/audio-only-transcription.ts +187 -292
  53. package/corpus/templates/clips/actions/lib/chunked-transcription.ts +151 -0
  54. package/corpus/templates/clips/actions/lib/recording-audio-source.ts +191 -0
  55. package/corpus/templates/clips/actions/request-transcript.ts +81 -135
  56. package/corpus/templates/clips/actions/search-recordings.ts +19 -5
  57. package/corpus/templates/clips/app/components/clips-command-menu.tsx +5 -0
  58. package/corpus/templates/clips/app/components/library/library-layout.tsx +3 -1
  59. package/corpus/templates/clips/app/components/library/search-bar.tsx +5 -0
  60. package/corpus/templates/clips/app/hooks/use-library.ts +1 -0
  61. package/corpus/templates/clips/app/lib/local-recording-upload.ts +13 -6
  62. package/corpus/templates/clips/app/lib/recording-section.ts +1 -1
  63. package/corpus/templates/clips/app/routes/_app.r.$recordingId.tsx +7 -5
  64. package/corpus/templates/clips/app/routes/record.tsx +16 -6
  65. package/corpus/templates/clips/changelog/2026-10-02-long-uploads-an-hour-or-more-now-transcribe-reliably-instead.md +6 -0
  66. package/corpus/templates/clips/changelog/2026-10-05-confirming-discard-recording-now-returns-you-to-your-library.md +6 -0
  67. package/corpus/templates/clips/changelog/2026-10-05-discarding-a-recording-now-removes-its-failed-upload-card-fr.md +6 -0
  68. package/corpus/templates/clips/changelog/2026-10-05-discarding-a-recovered-recording-no-longer-warns-that-it-s-o.md +6 -0
  69. package/corpus/templates/clips/changelog/2026-10-05-search-results-identify-recordings-in-trash-and-open-them-wi.md +6 -0
  70. package/corpus/templates/clips/package.json +1 -0
  71. package/corpus/templates/clips/server/lib/builder-cdn.ts +5 -0
  72. package/corpus/templates/clips/server/lib/builder-media-compression.ts +2 -2
  73. package/corpus/templates/clips/server/plugins/auth.ts +2 -1
  74. package/corpus/templates/clips/server/plugins/otel.ts +6 -0
  75. package/corpus/templates/clips/shared/transcript-failure.ts +5 -0
  76. package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +8 -2
  77. package/corpus/templates/content/.agents/skills/security/SKILL.md +9 -2
  78. package/corpus/templates/content/.agents/skills/storing-data/SKILL.md +12 -7
  79. package/corpus/templates/content/CHANGELOG.md +41 -0
  80. package/corpus/templates/content/README.md +2 -2
  81. package/corpus/templates/content/actions/_database-navigation.ts +523 -344
  82. package/corpus/templates/content/actions/_database-row-mutation.ts +44 -10
  83. package/corpus/templates/content/actions/_database-setup-discovery.ts +9 -3
  84. package/corpus/templates/content/actions/_database-utils.ts +299 -271
  85. package/corpus/templates/content/actions/_property-utils.ts +293 -190
  86. package/corpus/templates/content/actions/_relation-values.ts +3 -1
  87. package/corpus/templates/content/actions/get-content-database.ts +1 -0
  88. package/corpus/templates/content/actions/query-content-database-items.ts +32 -5
  89. package/corpus/templates/content/actions/set-document-property.ts +17 -11
  90. package/corpus/templates/content/app/components/editor/DocumentBlockFields.tsx +21 -6
  91. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +127 -43
  92. package/corpus/templates/content/app/components/editor/DocumentEditorSkeleton.tsx +159 -50
  93. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +12 -28
  94. package/corpus/templates/content/app/components/editor/PageDraftRecovery.tsx +6 -1
  95. package/corpus/templates/content/app/components/editor/TableHoverControls.tsx +139 -70
  96. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +16 -1
  97. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +180 -75
  98. package/corpus/templates/content/app/components/editor/database/DatabaseViewSkeleton.tsx +76 -0
  99. package/corpus/templates/content/app/components/editor/database/navigation-state.ts +60 -2
  100. package/corpus/templates/content/app/components/editor/database/sidebar.tsx +41 -42
  101. package/corpus/templates/content/app/components/editor/document-editor-layout.ts +18 -0
  102. package/corpus/templates/content/app/components/editor/extensions/NotionExtensions.tsx +11 -3
  103. package/corpus/templates/content/app/components/editor/suggestions/draft-session.ts +15 -7
  104. package/corpus/templates/content/app/components/editor/suggestions/model.ts +13 -0
  105. package/corpus/templates/content/app/components/editor/suggestions/read-only-blocks.ts +136 -0
  106. package/corpus/templates/content/app/components/editor/table-view.ts +73 -0
  107. package/corpus/templates/content/app/components/layout/ContentStartupShell.tsx +1 -1
  108. package/corpus/templates/content/app/components/layout/Layout.tsx +5 -1
  109. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +57 -16
  110. package/corpus/templates/content/app/components/sidebar/MovePageDialog.tsx +4 -4
  111. package/corpus/templates/content/app/components/sidebar/SidebarNavigationRow.tsx +34 -10
  112. package/corpus/templates/content/app/global.css +16 -8
  113. package/corpus/templates/content/app/hooks/content-action-refresh.ts +14 -13
  114. package/corpus/templates/content/app/hooks/use-content-database.ts +72 -9
  115. package/corpus/templates/content/app/hooks/use-content-personal-navigation.ts +7 -3
  116. package/corpus/templates/content/app/hooks/use-documents.ts +27 -0
  117. package/corpus/templates/content/app/i18n-data.ts +3 -2
  118. package/corpus/templates/content/app/lib/content-landing.ts +3 -31
  119. package/corpus/templates/content/app/lib/files-navigation.ts +182 -10
  120. package/corpus/templates/content/app/lib/files-root-hint.ts +20 -32
  121. package/corpus/templates/content/app/lib/page-startup-hints.ts +108 -0
  122. package/corpus/templates/content/app/lib/private-link-redirect.ts +25 -0
  123. package/corpus/templates/content/app/lib/sidebar-layout-hint.ts +34 -1
  124. package/corpus/templates/content/app/lib/startup-timing.ts +2 -0
  125. package/corpus/templates/content/app/root.tsx +3 -1
  126. package/corpus/templates/content/app/routes/_app.home.tsx +10 -19
  127. package/corpus/templates/content/app/routes/p.$id.tsx +23 -5
  128. package/corpus/templates/content/changelog/2026-10-02-a-page-link-you-cant-open-now-says-so-instead-of-opening-an.md +6 -0
  129. package/corpus/templates/content/changelog/2026-10-02-after-a-refresh-the-files-sidebar-shows-the-open-page-s-fold.md +6 -0
  130. package/corpus/templates/content/changelog/2026-10-02-large-collections-open-faster.md +6 -0
  131. package/corpus/templates/content/changelog/2026-10-02-pages-stay-inside-the-window-at-every-width-long-inline-code.md +6 -0
  132. package/corpus/templates/content/changelog/2026-10-02-pages-with-open-comments-and-collections-with-sorted-or-filt.md +6 -0
  133. package/corpus/templates/content/changelog/2026-10-02-suggest-edits-works-on-pages-written-by-agents-including-pag.md +6 -0
  134. package/corpus/templates/content/changelog/2026-10-02-while-you-suggest-edits-tables-and-other-blocks-that-suggest.md +6 -0
  135. package/corpus/templates/content/changelog/2026-10-05-collection-view-context-now-survives-quick-page-changes-when.md +6 -0
  136. package/corpus/templates/content/changelog/2026-10-05-collection-views-keep-their-saved-rows-accurate-during-refre.md +6 -0
  137. package/corpus/templates/content/changelog/2026-10-05-exact-saved-view-links-keep-collection-retry-available.md +6 -0
  138. package/corpus/templates/content/changelog/2026-10-05-expanding-a-files-folder-now-shares-one-navigation-read-acro.md +6 -0
  139. package/corpus/templates/content/changelog/2026-10-05-files-navigation-retries-temporary-failures-consistently.md +6 -0
  140. package/corpus/templates/content/changelog/2026-10-05-shared-page-owners-keep-edit-and-manage-controls-in-files-an.md +6 -0
  141. package/corpus/templates/content/changelog/2026-10-05-tagging-an-ai-in-a-comment-works-again-instead-of-failing-ri.md +6 -0
  142. package/corpus/templates/content/changelog/2026-10-05-the-home-loading-shell-keeps-the-layout-of-your-last-opened-.md +6 -0
  143. package/corpus/templates/content/changelog/2026-10-05-typing-and-a-space-or-inserting-a-toggle-or-callout-no-longe.md +6 -0
  144. package/corpus/templates/content/docs/product/capabilities/content.workspace.session-restore.md +4 -1
  145. package/corpus/templates/content/e2e/playwright.config.ts +1 -1
  146. package/corpus/templates/content/package.json +2 -1
  147. package/corpus/templates/content/scripts/seed-perf-database.mjs +600 -0
  148. package/corpus/templates/content/scripts/trace-startup.mjs +25 -3
  149. package/corpus/templates/content/server/db/index.ts +1 -1
  150. package/corpus/templates/content/server/lib/suggested-edits.ts +23 -26
  151. package/corpus/templates/content/server/plugins/auth.ts +2 -1
  152. package/corpus/templates/content/server/plugins/otel.ts +6 -0
  153. package/corpus/templates/content/shared/api.ts +17 -0
  154. package/corpus/templates/content/shared/suggestion-diff.ts +124 -38
  155. package/corpus/templates/content/shared/suggestion-formatting.ts +134 -3
  156. package/corpus/templates/crm/.agents/skills/security/SKILL.md +9 -2
  157. package/corpus/templates/crm/.agents/skills/storing-data/SKILL.md +12 -7
  158. package/corpus/templates/crm/package.json +1 -0
  159. package/corpus/templates/crm/server/plugins/otel.ts +6 -0
  160. package/corpus/templates/design/.agents/skills/security/SKILL.md +9 -2
  161. package/corpus/templates/design/.agents/skills/storing-data/SKILL.md +12 -7
  162. package/corpus/templates/design/CHANGELOG.md +41 -0
  163. package/corpus/templates/design/README.md +2 -2
  164. package/corpus/templates/design/actions/list-designs.ts +11 -4
  165. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +110 -32
  166. package/corpus/templates/design/app/components/design/multi-screen/cross-screen-drop.ts +28 -0
  167. package/corpus/templates/design/app/components/design/multi-screen/types.ts +1 -0
  168. package/corpus/templates/design/app/components/home/DesignHomeLibrary.tsx +79 -0
  169. package/corpus/templates/design/app/components/layout/Header.tsx +2 -4
  170. package/corpus/templates/design/app/components/ui/tabs.tsx +59 -1
  171. package/corpus/templates/design/app/global.css +1 -34
  172. package/corpus/templates/design/app/i18n/en-US.ts +4 -0
  173. package/corpus/templates/design/app/lib/design-filter.ts +4 -2
  174. package/corpus/templates/design/app/pages/Index.tsx +91 -59
  175. package/corpus/templates/design/app/pages/design-editor/commands/cross-screen-element-drop.ts +58 -17
  176. package/corpus/templates/design/changelog/2026-10-01-filter-designs-by-owner.md +6 -0
  177. package/corpus/templates/design/changelog/2026-10-03-layers-dropped-into-visible-canvas-space-beyond-a-screen-now.md +6 -0
  178. package/corpus/templates/design/changelog/2026-10-03-visible-screen-overflow-no-longer-routes-drops-to-the-canvas.md +6 -0
  179. package/corpus/templates/design/changelog/2026-10-05-the-recent-tab-shows-loading-placeholders-until-your-designs.md +6 -0
  180. package/corpus/templates/design/package.json +1 -0
  181. package/corpus/templates/design/server/plugins/auth.ts +2 -1
  182. package/corpus/templates/design/server/plugins/otel.ts +6 -0
  183. package/corpus/templates/dispatch/.agents/skills/security/SKILL.md +9 -2
  184. package/corpus/templates/dispatch/.agents/skills/storing-data/SKILL.md +12 -7
  185. package/corpus/templates/dispatch/README.md +2 -2
  186. package/corpus/templates/dispatch/package.json +1 -0
  187. package/corpus/templates/dispatch/server/plugins/otel.ts +6 -0
  188. package/corpus/templates/dispatch/server/plugins/setup-dispatch.ts +2 -1
  189. package/corpus/templates/factory/.agents/skills/security/SKILL.md +9 -2
  190. package/corpus/templates/factory/.agents/skills/storing-data/SKILL.md +12 -7
  191. package/corpus/templates/factory/package.json +1 -0
  192. package/corpus/templates/factory/server/plugins/otel.ts +6 -0
  193. package/corpus/templates/forms/.agents/skills/security/SKILL.md +9 -2
  194. package/corpus/templates/forms/.agents/skills/storing-data/SKILL.md +12 -7
  195. package/corpus/templates/forms/README.md +2 -2
  196. package/corpus/templates/forms/package.json +1 -0
  197. package/corpus/templates/forms/server/plugins/auth.ts +2 -1
  198. package/corpus/templates/forms/server/plugins/otel.ts +6 -0
  199. package/corpus/templates/mail/.agents/skills/security/SKILL.md +9 -2
  200. package/corpus/templates/mail/.agents/skills/storing-data/SKILL.md +12 -7
  201. package/corpus/templates/mail/CHANGELOG.md +6 -0
  202. package/corpus/templates/mail/README.md +2 -2
  203. package/corpus/templates/mail/package.json +1 -0
  204. package/corpus/templates/mail/server/plugins/auth.ts +2 -1
  205. package/corpus/templates/mail/server/plugins/otel.ts +6 -0
  206. package/corpus/templates/plan/.agents/skills/security/SKILL.md +9 -2
  207. package/corpus/templates/plan/.agents/skills/storing-data/SKILL.md +12 -7
  208. package/corpus/templates/plan/CHANGELOG.md +1 -0
  209. package/corpus/templates/plan/README.md +2 -2
  210. package/corpus/templates/plan/package.json +1 -0
  211. package/corpus/templates/plan/server/plugins/auth.ts +2 -1
  212. package/corpus/templates/plan/server/plugins/otel.ts +6 -0
  213. package/corpus/templates/slides/.agents/skills/security/SKILL.md +9 -2
  214. package/corpus/templates/slides/.agents/skills/storing-data/SKILL.md +12 -7
  215. package/corpus/templates/slides/CHANGELOG.md +23 -0
  216. package/corpus/templates/slides/README.md +2 -2
  217. package/corpus/templates/slides/actions/list-decks.ts +11 -4
  218. package/corpus/templates/slides/app/components/deck/DeckFilterMenu.tsx +44 -36
  219. package/corpus/templates/slides/app/components/home/SlidesHomeLibrary.tsx +79 -0
  220. package/corpus/templates/slides/app/components/layout/Header.tsx +2 -15
  221. package/corpus/templates/slides/app/components/ui/tabs.tsx +59 -0
  222. package/corpus/templates/slides/app/global.css +3 -54
  223. package/corpus/templates/slides/app/i18n/en-US.ts +4 -0
  224. package/corpus/templates/slides/app/lib/deck-filter.ts +5 -2
  225. package/corpus/templates/slides/app/pages/Index.tsx +102 -88
  226. package/corpus/templates/slides/changelog/2026-10-01-filter-decks-by-owner.md +6 -0
  227. package/corpus/templates/slides/changelog/2026-10-02-hide-recent-until-decks-available.md +6 -0
  228. package/corpus/templates/slides/changelog/2026-10-05-the-recent-tab-shows-loading-placeholders-until-your-decks-a.md +6 -0
  229. package/corpus/templates/slides/package.json +1 -0
  230. package/corpus/templates/slides/server/handlers/request-auth-context.ts +8 -0
  231. package/corpus/templates/slides/server/plugins/auth.ts +2 -1
  232. package/corpus/templates/slides/server/plugins/otel.ts +6 -0
  233. package/corpus/templates/tasks/.agents/skills/security/SKILL.md +9 -2
  234. package/corpus/templates/tasks/.agents/skills/storing-data/SKILL.md +12 -7
  235. package/package.json +1 -1
package/corpus/README.md CHANGED
@@ -32,4 +32,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core-corpus/corpu
32
32
 
33
33
  ## Generated Counts
34
34
 
35
- - template files: 11091
35
+ - template files: 11150
@@ -290,9 +290,16 @@ export default defineEventHandler(async (event) => {
290
290
 
291
291
  ## Data Scoping
292
292
 
293
- In production, the framework automatically restricts all agent SQL queries to the current user's data using temporary views. This is enforced at the SQL level — the agent cannot bypass it.
293
+ In production, the framework automatically restricts all agent SQL queries to the current user's data using temporary views, and checks every statement before it runs so it can only reach those views.
294
294
 
295
- The `db-query` / `db-exec` tools (and the extension SQL bridge, which shares the same path) reject schema-qualified table references like `public.<table>` — a qualified name resolves to the base table and would skip the temp view. Use bare table names; scoping is applied automatically.
295
+ The `db-query` / `db-exec` / `db-patch` tools (and the extension SQL bridge, which shares the same path) read each statement the way Postgres does and refuse anything that could reach data around the temp views:
296
+
297
+ - schema- or database-qualified names like `public.<table>`, which resolve to the base table;
298
+ - names that resolve to anything other than the current user's temp views, such as materialized views or tables the scoping does not cover;
299
+ - app-defined SQL or PL/pgSQL functions and operators, whose bodies read tables without the views;
300
+ - built-ins that run SQL text, change session settings, or reach outside the database (`query_to_xml`, `set_config`, `dblink`, most `pg_*` functions).
301
+
302
+ `db-query` runs in a read-only transaction, and an `INSERT` must list its columns when the table has access-control columns. Use bare table names and plain expressions; scoping is applied automatically. When the agent needs data the views do not expose, add an app action with its own access checks.
296
303
 
297
304
  ### Per-User Scoping (`owner_email`)
298
305
 
@@ -88,10 +88,20 @@ agent answers about browser recordings in the Analytics template.
88
88
  The catalog keeps the 1,000 most recently seen events, sorted by volume, and
89
89
  sets `truncated` when it cut the list; its app flags still count every event
90
90
  in the range.
91
- - Unique indexes hold caller text raw, so it must stay short enough for an
92
- index entry: index rows use hashed ids, event names and apps are cut to 200
93
- and 100 characters, and a session id over 256 characters skips the index.
94
- Bound any new caller value before it reaches a key.
91
+ - Indexes hold caller text raw, and one entry over Postgres's limit fails the
92
+ whole batch. Ingest cuts every indexed value with
93
+ `server/lib/indexed-text.ts` (event name, app, template, path) before any
94
+ table sees it. A long user key keeps a prefix plus a hash of the whole value
95
+ (`boundedIdentity`), so two users never merge into one. Row ids built from
96
+ caller text go through `indexedRowId`, which hashes an id that
97
+ percent-encoding made too long. Session and recording ids are never cut,
98
+ because a cut id could merge two sessions: replay ingest rejects session and
99
+ recording ids over 256 characters, and an event's longer session id skips the
100
+ session index. Bound any new indexed caller value there.
101
+ - Public ingest (`/track`, `/api/analytics/replay`) returns a thrown message
102
+ only for an error built with `requestError` (a numeric `statusCode`). Any
103
+ other failure is logged and answered with a generic 500, because its message
104
+ can quote internal database details.
95
105
  - Event filters exclude a session if any of its recordings started before the
96
106
  tenant's coverage start, because one analytics session can span tabs.
97
107
  Coverage starts only after a session write succeeds, and the reported start
@@ -75,10 +75,11 @@ state their PostgreSQL types directly.
75
75
 
76
76
  Member offboarding and email changes refuse to run while any column named
77
77
  `email`, `*_email`, `*scope_id`, `created_by`, `updated_by`, `invited_by`,
78
- `owner`, `principal_id`, `session_id`, or `user_id` has no policy. `owner_email`
79
- and `createSharesTable()` tables are handled for you. Declare every other one,
80
- including columns that are not member identities, from the app's database
81
- plugin graph (Clips does it in `server/db/index.ts`):
78
+ `owner`, `principal_id`, `session_id`, or `user_id` has no policy.
79
+ `createSharesTable()` tables are handled for you, and undeclared `owner_email`
80
+ columns transfer to the successor, which is right only for owned content.
81
+ Declare every other one, including columns that are not member identities, from
82
+ the app's database plugin graph (Clips does it in `server/db/index.ts`):
82
83
 
83
84
  ```ts
84
85
  import { registerIdentityColumns } from "@agent-native/core/org";
@@ -88,13 +89,17 @@ registerIdentityColumns([
88
89
  { table: "space_members", column: "email", emailChange: "rekey", offboard: "delete", orgScope: { column: "space_id", references: { table: "spaces", column: "id", orgColumn: "org_id" } }, reason: "Space membership grants access." },
89
90
  // Someone else's address: never rewritten.
90
91
  { table: "meeting_participants", column: "email", emailChange: "retain", offboard: "retain", reason: "Attendee address from the calendar provider." },
92
+ // A credential in an owner_email table: never handed to the successor.
93
+ { table: "api_keys", column: "owner_email", emailChange: "rekey", offboard: "revoke", reason: "Bearer keys act as their owner." },
91
94
  ]);
92
95
  ```
93
96
 
94
- Choose `delete` for grants, credentials, and pending tokens; `retain` for
97
+ Choose `delete` for grants, credentials, and pending tokens, or `revoke` (sets
98
+ `revoked_at`) when a reader treats a missing row as "not revoked"; `retain` for
95
99
  attribution, history, and third-party addresses; `transfer` only for owned
96
- data. A table without `org_id` needs `orgScope` or an organization-scoped
97
- removal leaves its rows alone.
100
+ data. Declare `owner_email` tables that hold credentials or grants, or
101
+ offboarding hands them to the successor. A table without `org_id` needs
102
+ `orgScope` or an organization-scoped removal leaves its rows alone.
98
103
 
99
104
  | Template | Tables |
100
105
  | ------------ | --------------------------------------------- |
@@ -7,8 +7,14 @@ time from the command menu (Cmd+K → "What's new") or from Settings.
7
7
 
8
8
  ### Improved
9
9
 
10
+ - Builder.io setup now explains free credits and the existing-account option.
11
+ - Owners and admins can save data source credentials for the whole organization or just for themselves.
10
12
  - Retention reports now compare returns from paid and untagged signups.
11
13
 
14
+ ### Fixed
15
+
16
+ - Events with a very long name, app, or page path no longer cause the rest of their batch to be lost
17
+
12
18
  ## 2026-10-01
13
19
 
14
20
  ### Improved
@@ -4,7 +4,7 @@ An open-source, agent-native alternative to Amplitude and FullStory. Ask
4
4
  analytics questions in plain English and get charts, dashboards, and re-runnable
5
5
  investigations back — you own the code, the queries, and the data.
6
6
 
7
- **Live app: [analytics.agent-native.com](https://analytics.agent-native.com)**
7
+ **Live app: [analytics.agent-native.com](https://analytics.agent-native.com?utm_source=github&utm_medium=referral&utm_content=analytics-readme)**
8
8
 
9
9
  The agent connects to your data, writes and validates the SQL for you, and
10
10
  renders the answer as a chart, table, metric, or saved dashboard panel. Anything
@@ -32,4 +32,4 @@ pnpm install
32
32
  pnpm dev
33
33
  ```
34
34
 
35
- Full docs: [agent-native.com/docs/template-analytics](https://agent-native.com/docs/template-analytics).
35
+ Full docs: [agent-native.com/docs/template-analytics](https://agent-native.com/docs/template-analytics?utm_source=github&utm_medium=referral&utm_content=analytics-readme).
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-02
4
+ ---
5
+
6
+ Events with a very long name, app, or page path no longer cause the rest of their batch to be lost
@@ -18,6 +18,7 @@
18
18
  "dependencies": {
19
19
  "@agent-native/core": "workspace:*",
20
20
  "@agent-native/creative-context": "workspace:*",
21
+ "@agent-native/otel": "workspace:*",
21
22
  "@agent-native/toolkit": "workspace:*",
22
23
  "@ai-sdk/openai": "^4.0.51",
23
24
  "@dnd-kit/core": "^6.3.1",
@@ -14,6 +14,7 @@ import {
14
14
  parseAnalyticsTrackPayload,
15
15
  recordAnalyticsEvents,
16
16
  } from "../lib/first-party-analytics.js";
17
+ import { errorReply } from "../lib/request-errors.js";
17
18
 
18
19
  function setCors(event: any): void {
19
20
  setResponseHeader(event, "Access-Control-Allow-Origin", "*");
@@ -39,18 +40,10 @@ export const handleAnalyticsTrack = defineEventHandler(async (event) => {
39
40
  return { success: true, accepted: 0 };
40
41
  }
41
42
  try {
42
- const headerKey = getHeader(event, "x-agent-native-analytics-key");
43
- let body = await readBody(event);
44
- if (headerKey) {
45
- if (typeof body === "string" && body.trim()) {
46
- body = { ...JSON.parse(body), publicKey: headerKey };
47
- } else if (body && typeof body === "object" && !Array.isArray(body)) {
48
- body = { ...(body as Record<string, unknown>), publicKey: headerKey };
49
- } else {
50
- body = { publicKey: headerKey };
51
- }
52
- }
53
- const parsed = parseAnalyticsTrackPayload(body);
43
+ const parsed = parseAnalyticsTrackPayload(
44
+ await readBody(event),
45
+ getHeader(event, "x-agent-native-analytics-key"),
46
+ );
54
47
  const result = await recordAnalyticsEvents(parsed.publicKey, parsed.events);
55
48
  setResponseStatus(event, 202);
56
49
  return {
@@ -58,12 +51,9 @@ export const handleAnalyticsTrack = defineEventHandler(async (event) => {
58
51
  accepted: result.accepted,
59
52
  suppressedTestIdentity: result.suppressedTestIdentity,
60
53
  };
61
- } catch (err: any) {
62
- const message = err?.message || String(err);
63
- const invalidKey = /invalid analytics public key/i.test(message);
64
- const statusCode =
65
- typeof err?.statusCode === "number" ? err.statusCode : undefined;
66
- setResponseStatus(event, invalidKey ? 401 : (statusCode ?? 400));
67
- return { error: message };
54
+ } catch (err) {
55
+ const reply = errorReply(err, "[first-party-analytics]");
56
+ setResponseStatus(event, reply.statusCode);
57
+ return { error: reply.error };
68
58
  }
69
59
  });
@@ -15,6 +15,11 @@ import {
15
15
  } from "h3";
16
16
 
17
17
  import { runApiHandlerWithContext } from "../lib/credentials";
18
+ import {
19
+ errorReply,
20
+ parseJsonBody,
21
+ requestError,
22
+ } from "../lib/request-errors.js";
18
23
  import {
19
24
  resolveSessionReplayAgentAccess,
20
25
  SESSION_REPLAY_AGENT_ACCESS_PARAM,
@@ -68,10 +73,6 @@ function setCors(event: any): void {
68
73
  setResponseHeader(event, "Access-Control-Expose-Headers", "retry-after");
69
74
  }
70
75
 
71
- function statusFromError(error: any): number {
72
- return typeof error?.statusCode === "number" ? error.statusCode : 400;
73
- }
74
-
75
76
  function retryAfterFromError(error: any): number | null {
76
77
  const seconds = error?.retryAfterSeconds;
77
78
  return typeof seconds === "number" && Number.isFinite(seconds) && seconds >= 0
@@ -79,8 +80,10 @@ function retryAfterFromError(error: any): number | null {
79
80
  : null;
80
81
  }
81
82
 
82
- function messageFromError(error: any): string {
83
- return error?.message || String(error);
83
+ function replyWithError(event: any, error: unknown): { error: string } {
84
+ const reply = errorReply(error, "[session-replay]");
85
+ setResponseStatus(event, reply.statusCode);
86
+ return { error: reply.error };
84
87
  }
85
88
 
86
89
  function hasQueryKey(query: Record<string, unknown>): boolean {
@@ -90,7 +93,7 @@ function hasQueryKey(query: Record<string, unknown>): boolean {
90
93
  function injectHeaderKey(body: unknown, headerKey?: string): unknown {
91
94
  if (!headerKey) return body;
92
95
  if (typeof body === "string" && body.trim()) {
93
- return { ...JSON.parse(body), publicKey: headerKey };
96
+ return { ...(parseJsonBody(body) as object), publicKey: headerKey };
94
97
  }
95
98
  if (body && typeof body === "object" && !Array.isArray(body)) {
96
99
  return { ...(body as Record<string, unknown>), publicKey: headerKey };
@@ -98,10 +101,6 @@ function injectHeaderKey(body: unknown, headerKey?: string): unknown {
98
101
  return { publicKey: headerKey };
99
102
  }
100
103
 
101
- function statusError(message: string, statusCode: number): Error {
102
- return Object.assign(new Error(message), { statusCode });
103
- }
104
-
105
104
  function looksLikeDecodedJson(bytes: Buffer): boolean {
106
105
  const first = bytes.toString("utf8").trimStart()[0];
107
106
  return first === "{" || first === "[";
@@ -156,12 +155,12 @@ export function decodeSessionReplayRequestBody(
156
155
  decoded = textWrappedGzip.decoded;
157
156
  requestBytes = textWrappedGzip.requestBytes;
158
157
  } else {
159
- throw statusError("Invalid gzip-compressed replay body", 400);
158
+ throw requestError("Invalid gzip-compressed replay body", 400);
160
159
  }
161
160
  }
162
161
  }
163
162
  } else if (encoding && encoding !== "identity") {
164
- throw statusError(
163
+ throw requestError(
165
164
  `Unsupported replay request content-encoding: ${encoding}`,
166
165
  415,
167
166
  );
@@ -289,11 +288,9 @@ export const handleSessionReplayIngest = defineEventHandler(async (event) => {
289
288
  try {
290
289
  const query = getQuery(event);
291
290
  if (hasQueryKey(query)) {
292
- throw Object.assign(
293
- new Error(
294
- "Analytics public keys must be sent in the request body or x-agent-native-analytics-key header, not the query string",
295
- ),
296
- { statusCode: 400 },
291
+ throw requestError(
292
+ "Analytics public keys must be sent in the request body or x-agent-native-analytics-key header, not the query string",
293
+ 400,
297
294
  );
298
295
  }
299
296
 
@@ -313,8 +310,7 @@ export const handleSessionReplayIngest = defineEventHandler(async (event) => {
313
310
  if (retryAfter !== null) {
314
311
  setResponseHeader(event, "Retry-After", String(retryAfter));
315
312
  }
316
- setResponseStatus(event, statusFromError(error));
317
- return { error: messageFromError(error) };
313
+ return replyWithError(event, error);
318
314
  }
319
315
  });
320
316
 
@@ -326,9 +322,8 @@ export const handleSessionReplayList = defineEventHandler(async (event) => {
326
322
  listFiltersFromQuery(getQuery(event)),
327
323
  );
328
324
  return { recordings };
329
- } catch (error: any) {
330
- setResponseStatus(event, statusFromError(error));
331
- return { error: messageFromError(error) };
325
+ } catch (error) {
326
+ return replyWithError(event, error);
332
327
  }
333
328
  });
334
329
  });
@@ -347,9 +342,8 @@ export const handleSessionReplaySummary = defineEventHandler(async (event) => {
347
342
  orgId: ctx.orgId ?? null,
348
343
  });
349
344
  return { recording };
350
- } catch (error: any) {
351
- setResponseStatus(event, statusFromError(error));
352
- return { error: messageFromError(error) };
345
+ } catch (error) {
346
+ return replyWithError(event, error);
353
347
  }
354
348
  });
355
349
  });
@@ -373,9 +367,8 @@ export const handleSessionReplayEvents = defineEventHandler(async (event) => {
373
367
  limit: asInt(query.limit),
374
368
  },
375
369
  );
376
- } catch (error: any) {
377
- setResponseStatus(event, statusFromError(error));
378
- return { error: messageFromError(error) };
370
+ } catch (error) {
371
+ return replyWithError(event, error);
379
372
  }
380
373
  });
381
374
  });
@@ -409,9 +402,8 @@ export const handleSessionReplayManifest = defineEventHandler(async (event) => {
409
402
  ),
410
403
  })),
411
404
  };
412
- } catch (error: any) {
413
- setResponseStatus(event, statusFromError(error));
414
- return { error: messageFromError(error) };
405
+ } catch (error) {
406
+ return replyWithError(event, error);
415
407
  }
416
408
  }
417
409
 
@@ -421,9 +413,8 @@ export const handleSessionReplayManifest = defineEventHandler(async (event) => {
421
413
  userEmail: ctx.userEmail,
422
414
  orgId: ctx.orgId ?? null,
423
415
  });
424
- } catch (error: any) {
425
- setResponseStatus(event, statusFromError(error));
426
- return { error: messageFromError(error) };
416
+ } catch (error) {
417
+ return replyWithError(event, error);
427
418
  }
428
419
  });
429
420
  });
@@ -454,9 +445,8 @@ export const handleSessionReplayChunkBytes = defineEventHandler(
454
445
  setResponseHeader(event, "X-Session-Replay-Seq", String(result.seq));
455
446
  setResponseHeader(event, "X-Session-Replay-Checksum", result.checksum);
456
447
  return result.json;
457
- } catch (error: any) {
458
- setResponseStatus(event, statusFromError(error));
459
- return { error: messageFromError(error) };
448
+ } catch (error) {
449
+ return replyWithError(event, error);
460
450
  }
461
451
  }
462
452
 
@@ -471,9 +461,8 @@ export const handleSessionReplayChunkBytes = defineEventHandler(
471
461
  setResponseHeader(event, "X-Session-Replay-Seq", String(result.seq));
472
462
  setResponseHeader(event, "X-Session-Replay-Checksum", result.checksum);
473
463
  return result.json;
474
- } catch (error: any) {
475
- setResponseStatus(event, statusFromError(error));
476
- return { error: messageFromError(error) };
464
+ } catch (error) {
465
+ return replyWithError(event, error);
477
466
  }
478
467
  });
479
468
  },
@@ -502,9 +491,8 @@ export const handleSessionReplayChunkBatch = defineEventHandler(
502
491
  applyAgentReplayReadHeaders(event);
503
492
  setResponseHeader(event, "Content-Type", "application/json");
504
493
  return result;
505
- } catch (error: any) {
506
- setResponseStatus(event, statusFromError(error));
507
- return { error: messageFromError(error) };
494
+ } catch (error) {
495
+ return replyWithError(event, error);
508
496
  }
509
497
  }
510
498
 
@@ -517,9 +505,8 @@ export const handleSessionReplayChunkBatch = defineEventHandler(
517
505
  setResponseHeader(event, "Content-Type", "application/json");
518
506
  setResponseHeader(event, "Cache-Control", "no-store");
519
507
  return result;
520
- } catch (error: any) {
521
- setResponseStatus(event, statusFromError(error));
522
- return { error: messageFromError(error) };
508
+ } catch (error) {
509
+ return replyWithError(event, error);
523
510
  }
524
511
  });
525
512
  },
@@ -1,6 +1,7 @@
1
1
  import { sql } from "@agent-native/core/db/schema";
2
2
 
3
3
  import { getDb, schema } from "../db/index.js";
4
+ import { indexedRowId } from "./indexed-text.js";
4
5
 
5
6
  export interface NormalizedFirstPartyAnalyticsEventRow {
6
7
  eventName: string;
@@ -63,10 +64,6 @@ function compositeKey(parts: readonly string[]): string {
63
64
  return JSON.stringify(parts);
64
65
  }
65
66
 
66
- function stableId(prefix: string, parts: readonly string[]): string {
67
- return `${prefix}_${parts.map((part) => encodeURIComponent(part)).join("|")}`;
68
- }
69
-
70
67
  export async function upsertFirstPartyAnalyticsRollups(
71
68
  rows: readonly NormalizedFirstPartyAnalyticsEventRow[],
72
69
  transaction?: any,
@@ -96,7 +93,13 @@ export async function upsertFirstPartyAnalyticsRollups(
96
93
  existingDaily.eventCount += 1;
97
94
  } else {
98
95
  dailyRollups.set(dailyKey, {
99
- id: stableId("aedr", [scopeKey, eventDate, eventName, app, template]),
96
+ id: indexedRowId("aedr", [
97
+ scopeKey,
98
+ eventDate,
99
+ eventName,
100
+ app,
101
+ template,
102
+ ]),
100
103
  tenantKey: scopeKey,
101
104
  ownerEmail,
102
105
  orgId,
@@ -112,7 +115,7 @@ export async function upsertFirstPartyAnalyticsRollups(
112
115
  const userDayKey = compositeKey([scopeKey, eventDate, userKey]);
113
116
  if (!userDays.has(userDayKey)) {
114
117
  userDays.set(userDayKey, {
115
- id: stableId("aud", [scopeKey, eventDate, userKey]),
118
+ id: indexedRowId("aud", [scopeKey, eventDate, userKey]),
116
119
  tenantKey: scopeKey,
117
120
  ownerEmail,
118
121
  orgId,
@@ -39,6 +39,15 @@ import {
39
39
  } from "./first-party-analytics-health.js";
40
40
  import { upsertFirstPartyAnalyticsRollups } from "./first-party-analytics-rollups.js";
41
41
  import { reserveFirstPartyPostgresEventVolume } from "./first-party-analytics-volume.js";
42
+ import {
43
+ MAX_APP_LENGTH,
44
+ MAX_EVENT_NAME_LENGTH,
45
+ MAX_PATH_LENGTH,
46
+ MAX_USER_KEY_LENGTH,
47
+ boundedIdentity,
48
+ boundedText,
49
+ } from "./indexed-text.js";
50
+ import { parseIngestBody, requestError } from "./request-errors.js";
42
51
  import {
43
52
  recordEventCatalog,
44
53
  recordSessionEventIndex,
@@ -488,14 +497,9 @@ function asRecord(value: unknown): Record<string, unknown> {
488
497
  : {};
489
498
  }
490
499
 
491
- const LONE_SURROGATE =
492
- /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
493
-
494
- // Rollup and index ids encode these values with encodeURIComponent, which
495
- // throws on a lone surrogate and would reject the batch on every retry.
496
500
  function asString(value: unknown): string | null {
497
501
  if (typeof value === "string" && value.trim()) {
498
- return value.trim().replace(LONE_SURROGATE, "\uFFFD");
502
+ return value.trim();
499
503
  }
500
504
  if (typeof value === "number" || typeof value === "boolean") {
501
505
  return String(value);
@@ -564,29 +568,35 @@ export function resolveAnalyticsEventDimensions({
564
568
  context: Record<string, unknown>;
565
569
  hostname: string | null;
566
570
  }): { app: string | null; template: string | null } {
567
- const app =
571
+ const app = boundedDimension(
568
572
  asString(properties.app_name) ||
569
- asString(properties.app) ||
570
- asString((properties as any).agent_native_app) ||
571
- asString((properties as any).agentNativeApp) ||
572
- asString((context as any).app) ||
573
- asString((context as any).agent_native_app) ||
574
- asString((context as any).agentNativeApp) ||
575
- (hostname ? hostname.split(".")[0] : null);
573
+ asString(properties.app) ||
574
+ asString((properties as any).agent_native_app) ||
575
+ asString((properties as any).agentNativeApp) ||
576
+ asString((context as any).app) ||
577
+ asString((context as any).agent_native_app) ||
578
+ asString((context as any).agentNativeApp) ||
579
+ (hostname ? hostname.split(".")[0] : null),
580
+ );
576
581
  const template =
577
- asString(properties.template_name) ||
578
- asString(properties.template) ||
579
- asString((properties as any).templateId) ||
580
- asString((properties as any).agent_native_template) ||
581
- asString((properties as any).agentNativeTemplate) ||
582
- asString((context as any).template) ||
583
- asString((context as any).templateId) ||
584
- asString((context as any).agent_native_template) ||
585
- asString((context as any).agentNativeTemplate) ||
586
- app;
582
+ boundedDimension(
583
+ asString(properties.template_name) ||
584
+ asString(properties.template) ||
585
+ asString((properties as any).templateId) ||
586
+ asString((properties as any).agent_native_template) ||
587
+ asString((properties as any).agentNativeTemplate) ||
588
+ asString((context as any).template) ||
589
+ asString((context as any).templateId) ||
590
+ asString((context as any).agent_native_template) ||
591
+ asString((context as any).agentNativeTemplate),
592
+ ) || app;
587
593
  return { app, template };
588
594
  }
589
595
 
596
+ function boundedDimension(value: string | null): string | null {
597
+ return value && boundedText(value, MAX_APP_LENGTH);
598
+ }
599
+
590
600
  export function isMarketingWebsiteSessionEvent({
591
601
  eventName,
592
602
  hostname,
@@ -616,35 +626,43 @@ export function isMarketingWebsiteSessionEvent({
616
626
  );
617
627
  }
618
628
 
619
- export function parseAnalyticsTrackPayload(raw: unknown): {
629
+ export function parseAnalyticsTrackPayload(
630
+ raw: unknown,
631
+ headerKey?: string | null,
632
+ ): {
620
633
  publicKey: string;
621
634
  events: IncomingAnalyticsEvent[];
622
635
  } {
623
- const body =
624
- typeof raw === "string" && raw.trim() ? JSON.parse(raw) : asRecord(raw);
636
+ const body = asRecord(parseIngestBody(raw));
625
637
  const publicKey =
638
+ asString(headerKey) ||
626
639
  asString((body as any).publicKey) ||
627
640
  asString((body as any).writeKey) ||
628
641
  asString((body as any).apiKey);
629
642
  if (!publicKey) {
630
- throw new Error("Missing publicKey");
643
+ throw requestError("Missing publicKey", 400);
631
644
  }
632
645
 
633
646
  const rawEvents = Array.isArray((body as any).events)
634
647
  ? (body as any).events
635
648
  : [body];
636
649
  if (rawEvents.length === 0) {
637
- throw new Error("No events provided");
650
+ throw requestError("No events provided", 400);
638
651
  }
639
652
  if (rawEvents.length > MAX_EVENTS_PER_REQUEST) {
640
- throw new Error(`At most ${MAX_EVENTS_PER_REQUEST} events are accepted`);
653
+ throw requestError(
654
+ `At most ${MAX_EVENTS_PER_REQUEST} events are accepted`,
655
+ 400,
656
+ );
641
657
  }
642
658
 
643
659
  const events = rawEvents.map((rawEvent: unknown) => {
644
660
  const obj = asRecord(rawEvent);
645
661
  const eventName =
646
662
  asString((obj as any).event) || asString((obj as any).name);
647
- if (!eventName) throw new Error("Each event requires an event name");
663
+ if (!eventName) {
664
+ throw requestError("Each event requires an event name", 400);
665
+ }
648
666
  return {
649
667
  event: eventName,
650
668
  properties: asRecord((obj as any).properties),
@@ -713,7 +731,7 @@ export async function recordAnalyticsEvents(
713
731
  )
714
732
  .limit(1);
715
733
  if (!key) {
716
- throw new Error("Invalid analytics public key");
734
+ throw requestError("Invalid analytics public key", 401);
717
735
  }
718
736
 
719
737
  const receivedAt = nowIso();
@@ -752,7 +770,9 @@ export async function recordAnalyticsEvents(
752
770
  event.anonymousId ??
753
771
  asString((properties as any).anonymousId) ??
754
772
  asString((properties as any).distinctId);
755
- const userKey = userId || anonymousId;
773
+ const rawUserKey = userId || anonymousId;
774
+ const userKey =
775
+ rawUserKey && boundedIdentity(rawUserKey, MAX_USER_KEY_LENGTH);
756
776
  const timestamp = normalizeAnalyticsTimestamp(event.timestamp, receivedAt);
757
777
  const sessionId =
758
778
  event.sessionId ??
@@ -775,7 +795,7 @@ export async function recordAnalyticsEvents(
775
795
  app,
776
796
  template,
777
797
  url: parts.url,
778
- userId,
798
+ userId: userId && boundedIdentity(userId, MAX_USER_KEY_LENGTH),
779
799
  anonymousId,
780
800
  userKey,
781
801
  sessionId,
@@ -789,10 +809,11 @@ export async function recordAnalyticsEvents(
789
809
  return [];
790
810
  }
791
811
 
812
+ const path = parts.path ?? asString(properties.path);
792
813
  return {
793
814
  id: id("evt"),
794
815
  publicKeyId: key.id,
795
- eventName: event.event,
816
+ eventName: boundedText(event.event, MAX_EVENT_NAME_LENGTH),
796
817
  userId,
797
818
  anonymousId,
798
819
  userKey,
@@ -801,7 +822,7 @@ export async function recordAnalyticsEvents(
801
822
  eventDate: eventDateFromTimestamp(timestamp),
802
823
  receivedAt,
803
824
  url: parts.url,
804
- path: parts.path ?? asString(properties.path),
825
+ path: path && boundedText(path, MAX_PATH_LENGTH),
805
826
  hostname,
806
827
  referrer:
807
828
  asString(properties.referrer) || asString((context as any).referrer),