@agent-native/core-corpus 0.216.0 → 0.218.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 (196) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +61 -1
  3. package/corpus/templates/analytics/AGENTS.md +1 -1
  4. package/corpus/templates/analytics/CHANGELOG.md +3 -0
  5. package/corpus/templates/analytics/actions/get-session-replay-summary.ts +9 -65
  6. package/corpus/templates/analytics/actions/list-event-catalog.ts +1 -1
  7. package/corpus/templates/analytics/actions/list-session-outputs.ts +40 -0
  8. package/corpus/templates/analytics/actions/list-session-recordings.ts +17 -4
  9. package/corpus/templates/analytics/actions/view-screen.ts +29 -3
  10. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +38 -167
  11. package/corpus/templates/analytics/app/i18n/en-US.ts +27 -0
  12. package/corpus/templates/analytics/app/pages/sessions/EventCatalogPage.tsx +18 -1
  13. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +3 -1
  14. package/corpus/templates/analytics/app/pages/sessions/SessionOutputs.tsx +197 -0
  15. package/corpus/templates/analytics/app/pages/sessions/SessionsTriagePage.tsx +114 -4
  16. package/corpus/templates/analytics/changelog/2026-10-15-sessions-can-find-people-who-made-a-slides-deck-but-didn-t-p.md +6 -0
  17. package/corpus/templates/analytics/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
  18. package/corpus/templates/analytics/changelog/2026-10-16-the-activation-funnel-counts-visitors-who-decline-analytics-.md +6 -0
  19. package/corpus/templates/analytics/server/db/schema.ts +67 -0
  20. package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +9 -0
  21. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +28 -22
  22. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +36 -1
  23. package/corpus/templates/analytics/server/lib/output-outcomes.ts +712 -0
  24. package/corpus/templates/analytics/server/lib/replay-origins.ts +33 -0
  25. package/corpus/templates/analytics/server/lib/session-event-index.ts +20 -0
  26. package/corpus/templates/analytics/server/lib/session-performance.ts +6 -40
  27. package/corpus/templates/analytics/server/lib/session-recording-associations.ts +30 -1
  28. package/corpus/templates/analytics/server/lib/session-replay-triage.ts +99 -0
  29. package/corpus/templates/analytics/server/lib/session-replay.ts +45 -31
  30. package/corpus/templates/analytics/server/lib/sessions-triage-lab.ts +8 -2
  31. package/corpus/templates/analytics/server/plugins/db.ts +46 -0
  32. package/corpus/templates/analytics/shared/output-use.ts +205 -0
  33. package/corpus/templates/analytics/shared/session-events.ts +6 -0
  34. package/corpus/templates/assets/CHANGELOG.md +6 -0
  35. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +51 -117
  36. package/corpus/templates/assets/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
  37. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +34 -101
  38. package/corpus/templates/brain/app/routes/sources.tsx +1 -1
  39. package/corpus/templates/brain/vitest.config.ts +7 -0
  40. package/corpus/templates/calendar/CHANGELOG.md +12 -0
  41. package/corpus/templates/calendar/actions/create-event.ts +2 -4
  42. package/corpus/templates/calendar/actions/update-event.ts +35 -6
  43. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +1 -1
  44. package/corpus/templates/calendar/changelog/2026-10-16-booking-links-show-localized-visibility-badges.md +6 -0
  45. package/corpus/templates/calendar/changelog/2026-10-21-solo-events-avoid-notification-approval.md +6 -0
  46. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +62 -101
  47. package/corpus/templates/clips/CHANGELOG.md +7 -20
  48. package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +20 -77
  49. package/corpus/templates/clips/app/components/player/share-dialog.tsx +63 -90
  50. package/corpus/templates/clips/app/components/sharing/share-test-support.tsx +180 -0
  51. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +5 -584
  52. package/corpus/templates/clips/changelog/2026-10-16-share-dialogs-make-it-easier-to-see-who-has-access-and-manag.md +6 -0
  53. package/corpus/templates/clips/changelog/2026-10-21-share-panels-describe-general-access-in-a-sentence.md +6 -0
  54. package/corpus/templates/community/qr/package.json +1 -1
  55. package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +6 -3
  56. package/corpus/templates/content/CHANGELOG.md +10 -0
  57. package/corpus/templates/content/actions/_browser-document-save-attempt.ts +23 -1
  58. package/corpus/templates/content/actions/_builder-cms-blob-custody.ts +467 -0
  59. package/corpus/templates/content/actions/_builder-cms-execution-lease.ts +17 -0
  60. package/corpus/templates/content/actions/_builder-cms-intent-lookup.ts +5 -0
  61. package/corpus/templates/content/actions/_builder-cms-read-client.ts +881 -21
  62. package/corpus/templates/content/actions/_builder-cms-write-adapter.ts +28 -2
  63. package/corpus/templates/content/actions/_builder-cms-write-client.ts +109 -11
  64. package/corpus/templates/content/actions/_builder-docs-client.ts +1 -0
  65. package/corpus/templates/content/actions/_builder-private-blob.test-fixture.ts +90 -0
  66. package/corpus/templates/content/actions/_builder-source-execution-guard.ts +48 -0
  67. package/corpus/templates/content/actions/_content-save-outcomes.ts +5 -0
  68. package/corpus/templates/content/actions/_database-source-utils.ts +290 -8
  69. package/corpus/templates/content/actions/_database-utils.ts +93 -7
  70. package/corpus/templates/content/actions/_database-view-setup.ts +21 -2
  71. package/corpus/templates/content/actions/_delete-content-space.ts +8 -1
  72. package/corpus/templates/content/actions/_github-source-freshness.ts +30 -4
  73. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +23 -6
  74. package/corpus/templates/content/actions/cancel-prepared-builder-source-update.ts +110 -26
  75. package/corpus/templates/content/actions/change-content-database-source-role.ts +51 -7
  76. package/corpus/templates/content/actions/delete-document.ts +41 -3
  77. package/corpus/templates/content/actions/disconnect-content-database-source.ts +68 -2
  78. package/corpus/templates/content/actions/disconnect-local-folder-source.ts +33 -2
  79. package/corpus/templates/content/actions/execute-builder-source-execution.ts +900 -140
  80. package/corpus/templates/content/actions/get-document.ts +2 -0
  81. package/corpus/templates/content/actions/permanently-delete-document.ts +6 -0
  82. package/corpus/templates/content/actions/prepare-builder-source-execution.ts +155 -13
  83. package/corpus/templates/content/actions/prepare-builder-source-review.ts +299 -73
  84. package/corpus/templates/content/actions/set-live-body-switch.ts +45 -0
  85. package/corpus/templates/content/actions/update-document.ts +139 -20
  86. package/corpus/templates/content/actions/validate-builder-source-execution.ts +135 -26
  87. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +119 -30
  88. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +56 -13
  89. package/corpus/templates/content/app/components/editor/SuggestionText.tsx +18 -3
  90. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +89 -22
  91. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +23 -0
  92. package/corpus/templates/content/app/components/editor/database/GitHubSourceStatusLine.tsx +27 -2
  93. package/corpus/templates/content/app/components/editor/database/ListItemLayout.tsx +32 -8
  94. package/corpus/templates/content/app/components/editor/database/ListItemRow.tsx +27 -2
  95. package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +1 -0
  96. package/corpus/templates/content/app/components/editor/extensions/SuggestionHighlight.ts +383 -34
  97. package/corpus/templates/content/app/components/editor/suggestions/draft-session.ts +31 -0
  98. package/corpus/templates/content/app/components/editor/suggestions/presentation-rebase.ts +82 -4
  99. package/corpus/templates/content/app/global.css +31 -0
  100. package/corpus/templates/content/app/hooks/use-content-database.ts +40 -2
  101. package/corpus/templates/content/app/hooks/use-documents.ts +14 -0
  102. package/corpus/templates/content/app/i18n-data.ts +1 -0
  103. package/corpus/templates/content/changelog/2026-10-16-accepting-a-suggestion-on-a-page-that-changed-since-it-was-s.md +6 -0
  104. package/corpus/templates/content/changelog/2026-10-17-builder-content-avoids-retrying-a-write-when-builder-accepte.md +6 -0
  105. package/corpus/templates/content/changelog/2026-10-18-link-a-new-collection-to-a-github-folder-from-its-sources.md +1 -1
  106. package/corpus/templates/content/changelog/2026-10-20-builder-content-sync-keeps-read-and-write-status-accurate-wh.md +6 -0
  107. package/corpus/templates/content/changelog/2026-10-20-suggested-edits-that-add-remove-split-or-merge-paragraphs-li.md +6 -0
  108. package/corpus/templates/content/changelog/2026-10-21-list-rows-can-show-each-pages-first-lines.md +6 -0
  109. package/corpus/templates/content/changelog/2026-10-22-a-collection-linked-to-a-big-github-folder-fills-faster-and-p.md +6 -0
  110. package/corpus/templates/content/docs/product/capabilities/content.source.adapters.md +29 -0
  111. package/corpus/templates/content/parity/matrix.md +42 -42
  112. package/corpus/templates/content/parity/matrix.ts +3 -1
  113. package/corpus/templates/content/server/db/schema.ts +20 -0
  114. package/corpus/templates/content/server/lib/content-trash-purge.ts +15 -1
  115. package/corpus/templates/content/server/lib/live-body-writer.ts +138 -26
  116. package/corpus/templates/content/server/live-copy/binding.ts +31 -0
  117. package/corpus/templates/content/server/live-copy/bounded-task.d.ts +6 -0
  118. package/corpus/templates/content/server/live-copy/exec-db.ts +1 -0
  119. package/corpus/templates/content/server/live-copy/holds.ts +59 -0
  120. package/corpus/templates/content/server/live-copy/persist.ts +656 -0
  121. package/corpus/templates/content/server/live-copy/switch.ts +136 -0
  122. package/corpus/templates/content/server/live-copy/task-names.ts +4 -0
  123. package/corpus/templates/content/server/live-copy/tasks.ts +183 -0
  124. package/corpus/templates/content/server/live-copy/write-cause.ts +85 -0
  125. package/corpus/templates/content/server/live-copy/write.ts +85 -0
  126. package/corpus/templates/content/server/plugins/builder-private-payload-cleanup.ts +22 -0
  127. package/corpus/templates/content/server/plugins/collab.ts +3 -0
  128. package/corpus/templates/content/server/plugins/db.ts +28 -0
  129. package/corpus/templates/content/shared/api.ts +30 -1
  130. package/corpus/templates/content/shared/database-item-layout.ts +54 -22
  131. package/corpus/templates/content/shared/document-intent-merge.ts +16 -9
  132. package/corpus/templates/content/shared/og-description.ts +258 -36
  133. package/corpus/templates/content/shared/suggestion-diff.ts +23 -6
  134. package/corpus/templates/content/shared/suggestion-formatting.ts +44 -14
  135. package/corpus/templates/content/shared/suggestion-text.ts +20 -5
  136. package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +5 -1
  137. package/corpus/templates/design/.generated/bridge/shader-runtime.generated.ts +25 -4
  138. package/corpus/templates/design/CHANGELOG.md +5 -0
  139. package/corpus/templates/design/actions/get-getting-started.ts +53 -0
  140. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +10 -0
  141. package/corpus/templates/design/app/components/design/bridge/native-shader-runtime.bridge.ts +132 -79
  142. package/corpus/templates/design/app/components/design/bridge/native-source-provider.ts +268 -130
  143. package/corpus/templates/design/app/components/design/bridge/native-worker-scene-host.ts +122 -42
  144. package/corpus/templates/design/app/components/design/bridge/shader-runtime.bridge.ts +41 -4
  145. package/corpus/templates/design/app/components/design/design-canvas/text-content-change-result.ts +46 -12
  146. package/corpus/templates/design/app/components/home/DesignHomeLibrary.tsx +15 -8
  147. package/corpus/templates/design/app/components/layout/GettingStarted.tsx +50 -0
  148. package/corpus/templates/design/app/components/layout/Sidebar.tsx +3 -0
  149. package/corpus/templates/design/app/components/templates/DesignTemplateGrid.tsx +163 -0
  150. package/corpus/templates/design/app/components/templates/DesignTemplateLibrary.tsx +65 -78
  151. package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +6 -2
  152. package/corpus/templates/design/app/global.css +6 -0
  153. package/corpus/templates/design/app/i18n/en-US.ts +17 -0
  154. package/corpus/templates/design/app/lib/canvas-load-outcome.ts +147 -0
  155. package/corpus/templates/design/app/lib/canvas-outcome-tracking.ts +531 -0
  156. package/corpus/templates/design/app/lib/canvas-style-kind.ts +50 -0
  157. package/corpus/templates/design/app/pages/DesignSystems.tsx +1 -1
  158. package/corpus/templates/design/app/pages/Index.tsx +28 -67
  159. package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +86 -29
  160. package/corpus/templates/design/app/pages/design-editor/commands/save-file-content.ts +38 -3
  161. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-core.ts +9 -0
  162. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-files-and-saving.ts +2 -0
  163. package/corpus/templates/design/changelog/2026-10-16-design-pages-use-localized-visibility-badges-and-a-shared-ow.md +6 -0
  164. package/corpus/templates/design/changelog/2026-10-21-a-getting-started-checklist-at-the-bottom-of-the-sidebar-tra.md +6 -0
  165. package/corpus/templates/design/changelog/2026-10-21-templates-now-open-a-read-only-preview-from-the-card-and-the.md +6 -0
  166. package/corpus/templates/design/changelog/2026-10-22-collapsed-shader-layers-no-longer-interrupt-other-shaders-on.md +6 -0
  167. package/corpus/templates/design/changelog/2026-10-22-shader-fills-keep-rendering-when-other-screen-content-cannot.md +6 -0
  168. package/corpus/templates/design/scripts/design-export-validation/export-baseline.json +24 -0
  169. package/corpus/templates/design/server/lib/getting-started.ts +267 -0
  170. package/corpus/templates/design/server/plugins/getting-started.ts +5 -0
  171. package/corpus/templates/design/shared/design-template-presets/analytics-dashboard.ts +127 -0
  172. package/corpus/templates/design/shared/design-template-presets/design-studio.ts +98 -0
  173. package/corpus/templates/design/shared/design-template-presets/messaging-app.ts +101 -0
  174. package/corpus/templates/design/shared/design-template-presets/project-tasks.ts +120 -0
  175. package/corpus/templates/design/shared/design-template-presets.ts +8 -0
  176. package/corpus/templates/dispatch/CHANGELOG.md +4 -0
  177. package/corpus/templates/dispatch/app/i18n-data.ts +75 -0
  178. package/corpus/templates/dispatch/changelog/2026-10-21-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
  179. package/corpus/templates/factory/app/components/layout/Sidebar.tsx +34 -100
  180. package/corpus/templates/forms/app/pages/FormsListPage.tsx +1 -1
  181. package/corpus/templates/forms/server/plugins/agent-chat.ts +1 -1
  182. package/corpus/templates/plan/CHANGELOG.md +6 -0
  183. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +34 -109
  184. package/corpus/templates/plan/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
  185. package/corpus/templates/slides/CHANGELOG.md +7 -0
  186. package/corpus/templates/slides/actions/delete-deck.ts +7 -0
  187. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +1 -1
  188. package/corpus/templates/slides/app/components/design-system/DesignSystemCard.tsx +1 -1
  189. package/corpus/templates/slides/app/pages/Index.tsx +7 -2
  190. package/corpus/templates/slides/changelog/2026-10-16-deck-lists-use-the-shared-ownership-filter-and-visibility-ba.md +6 -0
  191. package/corpus/templates/slides/changelog/2026-10-21-connected-deck-deletion-requires-approval.md +6 -0
  192. package/corpus/templates/slides/scripts/edit-fidelity/client-action.ts +61 -0
  193. package/corpus/templates/slides/scripts/edit-fidelity/run.ts +10 -0
  194. package/package.json +1 -1
  195. package/corpus/templates/chat/app/lib/sidebar-thread-state.ts +0 -29
  196. package/corpus/templates/slides/app/components/deck/DeckFilterMenu.tsx +0 -83
@@ -6,6 +6,7 @@ import {
6
6
  IconChevronLeft,
7
7
  IconChevronRight,
8
8
  IconGauge,
9
+ IconPackageOff,
9
10
  IconPhoto,
10
11
  IconRefresh,
11
12
  IconX,
@@ -44,6 +45,14 @@ import { useReplayStorageStatus } from "@/hooks/use-replay-storage-status";
44
45
  import { cn } from "@/lib/utils";
45
46
 
46
47
  import { ANALYTICS_SESSIONS_TRIAGE_LAB } from "../../../shared/labs";
48
+ import {
49
+ isOutputUseApp,
50
+ OUTPUT_USE_APPS,
51
+ OUTPUT_USE_WINDOW_DAYS,
52
+ type OutputUseApp,
53
+ type SessionOutput,
54
+ UNUSED_OUTPUT_PARAM,
55
+ } from "../../../shared/output-use";
47
56
  import {
48
57
  sessionDateBound,
49
58
  sessionDateForDisplay,
@@ -86,6 +95,11 @@ import {
86
95
  SessionFrictionFilter,
87
96
  SessionFrictionStrip,
88
97
  } from "./SessionFriction";
98
+ import {
99
+ UnusedOutputLink,
100
+ unusedFilterLabel,
101
+ unusedRuleText,
102
+ } from "./SessionOutputs";
89
103
  import { SessionReplayStoryboardExportDialog } from "./SessionReplayStoryboardExportDialog";
90
104
  import {
91
105
  EmptySessionsState,
@@ -128,6 +142,10 @@ type Page = {
128
142
  frictionCoverageStartedAt?: string | null;
129
143
  /** Present when a speed filter applied; null: nothing measured yet. */
130
144
  performanceCoverageStartedAt?: string | null;
145
+ /** Present when the unused filter applied, by recording id. */
146
+ outputs?: Record<string, SessionOutput[] | null>;
147
+ /** Present when the unused filter applied; null: nothing tracked yet. */
148
+ outputCoverageStartedAt?: string | null;
131
149
  };
132
150
 
133
151
  const RANGES: Range[] = ["24h", "7d", "30d", "90d", "all"];
@@ -303,11 +321,18 @@ export function SessionsTriagePage() {
303
321
  const slow: SlowSessionFilter | undefined =
304
322
  eventsLabEnabled && isSlowSessionFilter(urlSlow) ? urlSlow : undefined;
305
323
  const urlHasSlowFilter = isSlowSessionFilter(urlSlow);
306
- // A shared link with event, friction, or slow conditions waits briefly for
307
- // the Lab state instead of listing unfiltered sessions. Nothing else waits,
308
- // and a hung read stops holding the list and reads as failed.
324
+ const urlUnused = params.get(UNUSED_OUTPUT_PARAM);
325
+ const unused: OutputUseApp | undefined =
326
+ eventsLabEnabled && isOutputUseApp(urlUnused) ? urlUnused : undefined;
327
+ const urlHasUnusedFilter = isOutputUseApp(urlUnused);
328
+ // A shared link with event, friction, slow, or unused conditions waits
329
+ // briefly for the Lab state instead of listing unfiltered sessions. Nothing
330
+ // else waits, and a hung read stops holding the list and reads as failed.
309
331
  const urlHasLabFilters =
310
- urlHasEventConditions || urlHasFrictionParams || urlHasSlowFilter;
332
+ urlHasEventConditions ||
333
+ urlHasFrictionParams ||
334
+ urlHasSlowFilter ||
335
+ urlHasUnusedFilter;
311
336
  const [labWaitExpired, setLabWaitExpired] = useState(false);
312
337
  const waitsForLab = urlHasLabFilters && eventsLab.isLoading;
313
338
  useEffect(() => {
@@ -445,6 +470,7 @@ export function SessionsTriagePage() {
445
470
  frictionSignals: frictionSignals.length ? frictionSignals : undefined,
446
471
  includeFriction: frictionApplied || undefined,
447
472
  slow,
473
+ unused,
448
474
  sort,
449
475
  offset: (page - 1) * SESSION_PAGE_SIZE,
450
476
  limit: SESSION_PAGE_SIZE,
@@ -491,6 +517,18 @@ export function SessionsTriagePage() {
491
517
  : t("sessions.speedCoverageSince", {
492
518
  date: new Date(speedCoverageStartedAt).toLocaleDateString(),
493
519
  });
520
+ const outputCoverageStartedAt = data?.outputCoverageStartedAt;
521
+ const outputCoverageNote =
522
+ outputCoverageStartedAt === undefined
523
+ ? null
524
+ : outputCoverageStartedAt === null
525
+ ? t("sessions.outputCoverageStarting", {
526
+ days: String(OUTPUT_USE_WINDOW_DAYS),
527
+ })
528
+ : t("sessions.outputCoverageSince", {
529
+ date: new Date(outputCoverageStartedAt).toLocaleDateString(),
530
+ days: String(OUTPUT_USE_WINDOW_DAYS),
531
+ });
494
532
  const total = data?.total ?? 0;
495
533
  // Speed hints load beside the list, keyed on its rows, so turning the Lab
496
534
  // on adds them without fetching the list again.
@@ -539,6 +577,7 @@ export function SessionsTriagePage() {
539
577
  : undefined,
540
578
  frictionSignals: frictionSignals.length ? frictionSignals : undefined,
541
579
  slow,
580
+ unused,
542
581
  sort,
543
582
  limit: 1,
544
583
  },
@@ -909,6 +948,54 @@ export function SessionsTriagePage() {
909
948
  </PopoverContent>
910
949
  </Popover>
911
950
  ) : null}
951
+ {eventsLabEnabled ? (
952
+ <Popover>
953
+ <PopoverTrigger asChild>
954
+ <Button
955
+ variant={unused ? "secondary" : "outline"}
956
+ size="sm"
957
+ className={cn(
958
+ "h-8 border border-input font-normal",
959
+ !unused && "bg-transparent hover:bg-accent",
960
+ )}
961
+ >
962
+ <IconPackageOff />
963
+ {unused
964
+ ? unusedFilterLabel(unused, t)
965
+ : t("sessions.unusedOutputs")}
966
+ </Button>
967
+ </PopoverTrigger>
968
+ <PopoverContent align="start" className="w-72">
969
+ <div className="space-y-3">
970
+ <Select
971
+ value={unused ?? "all"}
972
+ onValueChange={(value) =>
973
+ setFilter(UNUSED_OUTPUT_PARAM, value === "all" ? "" : value)
974
+ }
975
+ >
976
+ <SelectTrigger aria-label={t("sessions.unusedOutputs")}>
977
+ <SelectValue />
978
+ </SelectTrigger>
979
+ <SelectContent>
980
+ <SelectItem value="all">
981
+ {t("sessions.anyOutcome")}
982
+ </SelectItem>
983
+ {OUTPUT_USE_APPS.map((value) => (
984
+ <SelectItem key={value} value={value}>
985
+ {unusedFilterLabel(value, t)}
986
+ </SelectItem>
987
+ ))}
988
+ </SelectContent>
989
+ </Select>
990
+ {OUTPUT_USE_APPS.map((value) => (
991
+ <p key={value} className="text-xs text-muted-foreground">
992
+ {unusedRuleText(value, t)}
993
+ </p>
994
+ ))}
995
+ </div>
996
+ </PopoverContent>
997
+ </Popover>
998
+ ) : null}
912
999
  {hasActiveFilters ? (
913
1000
  <Button
914
1001
  variant="ghost"
@@ -959,6 +1046,14 @@ export function SessionsTriagePage() {
959
1046
  {t("sessions.speedFilterNeedsLab")}
960
1047
  </p>
961
1048
  ) : null}
1049
+ {urlHasUnusedFilter &&
1050
+ !eventsLabEnabled &&
1051
+ !eventsLab.isLoading &&
1052
+ !eventsLab.isError ? (
1053
+ <p className="text-xs text-muted-foreground" role="status">
1054
+ {t("sessions.unusedFilterNeedsLab")}
1055
+ </p>
1056
+ ) : null}
962
1057
  {eventsLabEnabled &&
963
1058
  !frictionApplied &&
964
1059
  rowFrictionError &&
@@ -1012,6 +1107,13 @@ export function SessionsTriagePage() {
1012
1107
  {speedCoverageNote}
1013
1108
  </p>
1014
1109
  ) : null}
1110
+ {outputCoverageStartedAt !== undefined &&
1111
+ recordings.length > 0 &&
1112
+ rangePredatesCoverage(dateBounds.from, outputCoverageStartedAt) ? (
1113
+ <p className="text-xs text-muted-foreground" role="status">
1114
+ {outputCoverageNote}
1115
+ </p>
1116
+ ) : null}
1015
1117
  <Card>
1016
1118
  <div className="flex items-center justify-between gap-2 border-b px-4 py-2 text-sm">
1017
1119
  <div className="text-muted-foreground" aria-live="polite">
@@ -1099,6 +1201,9 @@ export function SessionsTriagePage() {
1099
1201
  {speedCoverageNote ? (
1100
1202
  <p className="mt-1 text-xs">{speedCoverageNote}</p>
1101
1203
  ) : null}
1204
+ {outputCoverageNote ? (
1205
+ <p className="mt-1 text-xs">{outputCoverageNote}</p>
1206
+ ) : null}
1102
1207
  {showEmptySessionRecovery ? (
1103
1208
  <Button
1104
1209
  variant="outline"
@@ -1273,6 +1378,11 @@ export function SessionsTriagePage() {
1273
1378
  />
1274
1379
  </span>
1275
1380
  </Link>
1381
+ {unused ? (
1382
+ <UnusedOutputLink
1383
+ outputs={data?.outputs?.[recording.id]}
1384
+ />
1385
+ ) : null}
1276
1386
  </div>
1277
1387
  </SessionRow>
1278
1388
  ))}
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-10-15
4
+ ---
5
+
6
+ Sessions can find people who made a Slides deck but didn't present or export it within 7 days, and the event catalog flags apps with no "used it" event (Sessions triage Lab)
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-10-16
4
+ ---
5
+
6
+ Owned and shared chats appear in separate history sections, with each shared chat's owner shown as an avatar and a Copy link action on every chat.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-16
4
+ ---
5
+
6
+ The activation funnel counts visitors who decline analytics cookies and then sign up in the same visit
@@ -654,6 +654,73 @@ export const analyticsPerformanceCoverage = table(
654
654
  },
655
655
  );
656
656
 
657
+ // Each output an app reported making, from `shared/output-use.ts`, and its
658
+ // first use. A null madeAt means only uses have arrived so far; useBy is
659
+ // madeAt plus the use window, so "used in time" compares two stored values.
660
+ export const analyticsOutputOutcomes = table(
661
+ "analytics_output_outcomes",
662
+ {
663
+ id: text("id").primaryKey(),
664
+ tenantKey: text("tenant_key").notNull(),
665
+ ownerEmail: text("owner_email").notNull(),
666
+ orgId: text("org_id"),
667
+ app: text("app").notNull(),
668
+ outputType: text("output_type").notNull(),
669
+ outputId: text("output_id").notNull(),
670
+ madeAt: text("made_at"),
671
+ useBy: text("use_by"),
672
+ madeSessionId: text("made_session_id"),
673
+ firstUsedAt: text("first_used_at"),
674
+ usedEvent: text("used_event"),
675
+ lastSeenAt: text("last_seen_at").notNull(),
676
+ },
677
+ (t) => ({
678
+ outputOutcomeUnique: uniqueIndex("analytics_output_outcomes_key_idx").on(
679
+ t.tenantKey,
680
+ t.app,
681
+ t.outputId,
682
+ ),
683
+ madeSessionIdx: index("analytics_output_outcomes_made_session_idx").on(
684
+ t.tenantKey,
685
+ t.madeSessionId,
686
+ ),
687
+ lastSeenIdx: index("analytics_output_outcomes_last_seen_idx").on(
688
+ t.lastSeenAt,
689
+ ),
690
+ }),
691
+ );
692
+
693
+ // Event-time spans in which a failed write may have dropped an output's use,
694
+ // so no output made in or before one can read as unused.
695
+ export const analyticsOutputOutcomeGaps = table(
696
+ "analytics_output_outcome_gaps",
697
+ {
698
+ id: text("id").primaryKey(),
699
+ tenantKey: text("tenant_key").notNull(),
700
+ ownerEmail: text("owner_email").notNull(),
701
+ orgId: text("org_id"),
702
+ firstAt: text("first_at").notNull(),
703
+ lastAt: text("last_at").notNull(),
704
+ recordedAt: text("recorded_at").notNull(),
705
+ },
706
+ (t) => ({
707
+ outputOutcomeGapTenantIdx: index(
708
+ "analytics_output_outcome_gaps_tenant_idx",
709
+ ).on(t.tenantKey, t.lastAt),
710
+ }),
711
+ );
712
+
713
+ // When each tenant's output outcomes began.
714
+ export const analyticsOutputOutcomeCoverage = table(
715
+ "analytics_output_outcome_coverage",
716
+ {
717
+ tenantKey: text("tenant_key").primaryKey(),
718
+ ownerEmail: text("owner_email").notNull(),
719
+ orgId: text("org_id"),
720
+ startedAt: text("started_at").notNull(),
721
+ },
722
+ );
723
+
657
724
  export const analyticsUserDays = table("analytics_user_days", {
658
725
  id: text("id").primaryKey(),
659
726
  tenantKey: text("tenant_key").notNull(),
@@ -651,6 +651,15 @@ const ORIGIN_MAIN_PANEL_REPLACEMENTS: readonly FingerprintedPanelReplacement[] =
651
651
  "ba2e861c2044e2fec669bcb15a878a675eaf426f51bcfe4db32ebd228d2050f0",
652
652
  sql: buildPanel("activation-funnel")!.sql,
653
653
  },
654
+ // The funnel before it joined visitors without analytics consent to
655
+ // their signup through the session.
656
+ {
657
+ id: "activation-funnel",
658
+ source: "first-party",
659
+ sha256:
660
+ "21df6cc1f26862531504c21fb5a558c87b58615118fcd0ad1913a971fa8387a6",
661
+ sql: buildPanel("activation-funnel")!.sql,
662
+ },
654
663
  {
655
664
  id: "retention-over-time",
656
665
  source: "bigquery",
@@ -74,6 +74,10 @@ import {
74
74
  boundedIdentity,
75
75
  boundedText,
76
76
  } from "./indexed-text.js";
77
+ import {
78
+ recordLostOutputBatch,
79
+ recordOutputOutcomes,
80
+ } from "./output-outcomes.js";
77
81
  import { parseIngestBody, requestError } from "./request-errors.js";
78
82
  import {
79
83
  recordEventCatalog,
@@ -243,6 +247,7 @@ async function persistBigQueryRowsWithMigrationFallback(
243
247
  );
244
248
  await recordSessionEventIndex(tx, rows, receivedAt);
245
249
  await recordSessionPerformance(tx, rows, receivedAt);
250
+ await recordOutputOutcomes(tx, rows, receivedAt);
246
251
  });
247
252
  } catch (error) {
248
253
  if (!isFirstPartyAnalyticsDeliveryQueueMissingError(error)) throw error;
@@ -269,6 +274,7 @@ async function persistBigQueryRowsWithMigrationFallback(
269
274
  }
270
275
  await recordSessionEventIndex(tx, rows, receivedAt);
271
276
  await recordSessionPerformance(tx, rows, receivedAt);
277
+ await recordOutputOutcomes(tx, rows, receivedAt);
272
278
  });
273
279
  try {
274
280
  const result = await runWithRequestContext(
@@ -948,31 +954,29 @@ export async function recordAnalyticsEvents(
948
954
  };
949
955
  });
950
956
 
951
- const backend = await getFirstPartyAnalyticsBackend({
952
- userEmail: key.ownerEmail,
953
- orgId: key.orgId ?? null,
954
- });
955
-
956
- if (rows.length && backend.sink === "dual") {
957
- try {
958
- await runWithRequestContext(
959
- {
960
- userEmail: key.ownerEmail,
961
- orgId: key.orgId ?? undefined,
962
- },
963
- () => insertFirstPartyAnalyticsRows(rows, backend.table),
964
- );
965
- } catch (error) {
966
- console.error(
967
- "[first-party-analytics] BigQuery dual-write failed; retaining Postgres event:",
968
- error,
969
- );
970
- }
971
- }
972
-
973
957
  let persistenceError: unknown = null;
974
958
  if (rows.length) {
975
959
  try {
960
+ const backend = await getFirstPartyAnalyticsBackend({
961
+ userEmail: key.ownerEmail,
962
+ orgId: key.orgId ?? null,
963
+ });
964
+ if (backend.sink === "dual") {
965
+ try {
966
+ await runWithRequestContext(
967
+ {
968
+ userEmail: key.ownerEmail,
969
+ orgId: key.orgId ?? undefined,
970
+ },
971
+ () => insertFirstPartyAnalyticsRows(rows, backend.table),
972
+ );
973
+ } catch (error) {
974
+ console.error(
975
+ "[first-party-analytics] BigQuery dual-write failed; retaining Postgres event:",
976
+ error,
977
+ );
978
+ }
979
+ }
976
980
  if (backend.sink === "bigquery") {
977
981
  await persistBigQueryRowsWithMigrationFallback(
978
982
  db,
@@ -998,10 +1002,12 @@ export async function recordAnalyticsEvents(
998
1002
  await upsertFirstPartyAnalyticsRollups(rows, tx);
999
1003
  await recordSessionEventIndex(tx, rows, receivedAt);
1000
1004
  await recordSessionPerformance(tx, rows, receivedAt);
1005
+ await recordOutputOutcomes(tx, rows, receivedAt);
1001
1006
  });
1002
1007
  }
1003
1008
  } catch (error) {
1004
1009
  persistenceError = error;
1010
+ await recordLostOutputBatch(db, rows, receivedAt);
1005
1011
  }
1006
1012
  }
1007
1013
  if (rows.length && !persistenceError) {
@@ -1022,22 +1022,57 @@ const FUNNEL_EVENTS_CTE = `WITH auth_identity_bridge AS (
1022
1022
  AND NULLIF(e.anonymous_id, '') IS NOT NULL
1023
1023
  GROUP BY NULLIF(e.anonymous_id, '')
1024
1024
  HAVING COUNT(DISTINCT NULLIF(e.properties::jsonb ->> 'auth_user_id', '')) <= 1
1025
+ ), signup_session_identity AS (
1026
+ -- Visitors in the consent region send no anonymous id until they accept
1027
+ -- analytics cookies, so their pre-signup events reach the signup only
1028
+ -- through the session they share with it.
1029
+ SELECT NULLIF(e.session_id, '') AS session_id,
1030
+ MIN(NULLIF(e.properties::jsonb ->> 'auth_user_id', '')) AS signup_auth_user_id,
1031
+ MIN(
1032
+ CASE
1033
+ WHEN NULLIF(e.user_id, '') LIKE '%@%.%' THEN e.user_id
1034
+ WHEN NULLIF(e.user_key, '') LIKE '%@%.%' THEN e.user_key
1035
+ END
1036
+ ) AS signup_user_email
1037
+ FROM analytics_events e
1038
+ WHERE e.event_name = 'signup'
1039
+ AND ${DASHBOARD_TIME_RANGE_FILTER}
1040
+ AND ${DASHBOARD_APP_FILTER}
1041
+ AND ${FIRST_PARTY_TEMPLATE_FILTER}
1042
+ AND NULLIF(e.session_id, '') IS NOT NULL
1043
+ GROUP BY NULLIF(e.session_id, '')
1044
+ -- A session with two signups can't say whose visit it was.
1045
+ HAVING COUNT(DISTINCT NULLIF(e.properties::jsonb ->> 'auth_user_id', '')) <= 1
1046
+ AND COUNT(
1047
+ DISTINCT CASE
1048
+ WHEN NULLIF(e.user_id, '') LIKE '%@%.%' THEN e.user_id
1049
+ WHEN NULLIF(e.user_key, '') LIKE '%@%.%' THEN e.user_key
1050
+ END
1051
+ ) <= 1
1025
1052
  ), raw_funnel_events AS (
1026
1053
  SELECT e.*,
1027
1054
  COALESCE(
1028
1055
  NULLIF(e.properties::jsonb ->> 'auth_user_id', ''),
1029
1056
  si.signup_auth_user_id,
1030
1057
  si.signup_user_email,
1058
+ ss.signup_auth_user_id,
1059
+ ss.signup_user_email,
1031
1060
  NULLIF(e.user_id, ''),
1032
1061
  NULLIF(e.anonymous_id, '')
1033
1062
  ) AS funnel_user_key,
1034
1063
  COALESCE(
1035
1064
  CASE WHEN NULLIF(e.user_id, '') LIKE '%@%.%' THEN e.user_id END,
1036
1065
  CASE WHEN NULLIF(e.user_key, '') LIKE '%@%.%' THEN e.user_key END,
1037
- si.signup_user_email
1066
+ si.signup_user_email,
1067
+ ss.signup_user_email
1038
1068
  ) AS funnel_user_email
1039
1069
  FROM analytics_events e
1040
1070
  LEFT JOIN signup_identity si ON si.anonymous_id = NULLIF(e.anonymous_id, '')
1071
+ LEFT JOIN signup_session_identity ss
1072
+ ON NULLIF(e.anonymous_id, '') IS NULL
1073
+ AND NULLIF(e.user_id, '') IS NULL
1074
+ AND NULLIF(e.properties::jsonb ->> 'auth_user_id', '') IS NULL
1075
+ AND ss.session_id = NULLIF(e.session_id, '')
1041
1076
  WHERE ${DASHBOARD_TIME_RANGE_FILTER}
1042
1077
  AND ${DASHBOARD_APP_FILTER}
1043
1078
  AND ${FIRST_PARTY_TEMPLATE_FILTER}