@agent-native/core 0.149.5 → 0.151.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 (197) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  3. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +5 -0
  4. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +83 -45
  5. package/corpus/templates/analytics/changelog/2026-08-11-analytics-dashboards-keep-the-selected-tab-when-reopened.md +6 -0
  6. package/corpus/templates/assets/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  7. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +5 -0
  8. package/corpus/templates/brain/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  9. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +5 -0
  10. package/corpus/templates/calendar/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  11. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +5 -0
  12. package/corpus/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  13. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +5 -0
  14. package/corpus/templates/clips/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  15. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +5 -0
  16. package/corpus/templates/clips/changelog/2026-08-11-clips-recovers-from-transient-updates-and-avoids-macos-priva.md +6 -0
  17. package/corpus/templates/clips/desktop/src/app.tsx +32 -176
  18. package/corpus/templates/clips/desktop/src/lib/updater.ts +52 -37
  19. package/corpus/templates/clips/desktop/src/styles.css +1 -166
  20. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +2 -0
  21. package/corpus/templates/clips/desktop/src-tauri/Info.plist +4 -0
  22. package/corpus/templates/clips/desktop/src-tauri/src/eventkit.rs +28 -1
  23. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +2 -0
  24. package/corpus/templates/clips/desktop/src-tauri/src/native_speech.rs +48 -1
  25. package/corpus/templates/clips/desktop/src-tauri/src/permission_status.rs +3 -0
  26. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +5 -0
  27. package/corpus/templates/clips/server/plugins/auth.ts +3 -0
  28. package/corpus/templates/content/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  29. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +5 -0
  30. package/corpus/templates/content/actions/_document-discovery-query.ts +127 -0
  31. package/corpus/templates/content/actions/list-documents.ts +189 -198
  32. package/corpus/templates/content/actions/search-documents.ts +95 -24
  33. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +2 -1
  34. package/corpus/templates/content/app/hooks/use-documents.ts +84 -6
  35. package/corpus/templates/content/shared/api.ts +10 -0
  36. package/corpus/templates/design/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  37. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +5 -0
  38. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +5 -1
  39. package/corpus/templates/design/changelog/2026-08-11-design-agent-prompts-copy-successfully-in-the-desktop-app.md +6 -0
  40. package/corpus/templates/dispatch/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  41. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +5 -0
  42. package/corpus/templates/dispatch/app/global.css +2 -2
  43. package/corpus/templates/dispatch/changelog/2026-08-10-desktop-workspace-sign-in.md +6 -0
  44. package/corpus/templates/dispatch/changelog/2026-08-11-dispatch-no-longer-shows-the-generic-chat-starter-among-defa.md +6 -0
  45. package/corpus/templates/dispatch/changelog/2026-08-11-integrations-and-scheduled-work-open-as-first-class-electron-pages.md +6 -0
  46. package/corpus/templates/dispatch/changelog/2026-08-11-sidebar-text-is-slightly-easier-to-read-in-light-and-dark-mo.md +6 -0
  47. package/corpus/templates/dispatch/server/lib/identity-sso.ts +57 -31
  48. package/corpus/templates/dispatch/server/plugins/feature-flags.ts +5 -0
  49. package/corpus/templates/dispatch/server/plugins/identity-sso.ts +82 -33
  50. package/corpus/templates/dispatch/server/plugins/org-apps-directory.ts +7 -15
  51. package/corpus/templates/dispatch/server/plugins/setup-dispatch.ts +9 -1
  52. package/corpus/templates/dispatch/shared/feature-flags.ts +15 -0
  53. package/corpus/templates/factory/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  54. package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +5 -0
  55. package/corpus/templates/forms/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  56. package/corpus/templates/forms/.agents/skills/form-responses/SKILL.md +8 -7
  57. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +5 -0
  58. package/corpus/templates/forms/app/hooks/use-forms.ts +8 -1
  59. package/corpus/templates/forms/changelog/2026-08-11-fixed-public-forms-so-conditional-questions-stay-hidden-unti.md +6 -0
  60. package/corpus/templates/forms/server/handlers/submissions.ts +2 -1
  61. package/corpus/templates/forms/server/lib/public-form-ssr.ts +37 -2
  62. package/corpus/templates/forms/shared/page-url.ts +54 -0
  63. package/corpus/templates/forms/shared/types.ts +4 -4
  64. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +5 -0
  65. package/corpus/templates/mail/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  66. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +5 -0
  67. package/corpus/templates/plan/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  68. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +5 -0
  69. package/corpus/templates/slides/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  70. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +5 -0
  71. package/corpus/templates/slides/actions/add-slide.ts +0 -27
  72. package/corpus/templates/slides/actions/import-file.ts +28 -9
  73. package/corpus/templates/slides/actions/view-screen.ts +4 -4
  74. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +8 -1
  75. package/corpus/templates/slides/app/components/editor/AiEditingMarker.tsx +25 -0
  76. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +43 -48
  77. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +9 -6
  78. package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +1 -9
  79. package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +2 -0
  80. package/corpus/templates/slides/app/components/editor/ImageOverlay.tsx +0 -26
  81. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +904 -217
  82. package/corpus/templates/slides/app/components/editor/canvas-interactions/index.ts +1 -0
  83. package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +11 -0
  84. package/corpus/templates/slides/app/components/editor/inline-edit-session.ts +16 -0
  85. package/corpus/templates/slides/app/components/editor/slide-object-interactions.ts +22 -6
  86. package/corpus/templates/slides/app/components/editor/style-clipboard.ts +61 -0
  87. package/corpus/templates/slides/app/components/layout/Layout.tsx +26 -2
  88. package/corpus/templates/slides/app/components/ui/context-menu.tsx +1 -0
  89. package/corpus/templates/slides/app/context/DeckContext.tsx +75 -25
  90. package/corpus/templates/slides/app/global.css +1 -1
  91. package/corpus/templates/slides/app/i18n/en-US.ts +6 -1
  92. package/corpus/templates/slides/app/lib/create-deck-generation.ts +1 -1
  93. package/corpus/templates/slides/app/lib/image-drop-to-agent.ts +12 -12
  94. package/corpus/templates/slides/app/lib/slide-agent-context.ts +52 -0
  95. package/corpus/templates/slides/app/lib/slide-image-replacement.ts +61 -0
  96. package/corpus/templates/slides/app/pages/DeckEditor.tsx +55 -116
  97. package/corpus/templates/slides/app/pages/Index.tsx +28 -3
  98. package/corpus/templates/slides/changelog/2026-08-11-agent-chat-now-follows-the-current-slide-and-selection.md +6 -0
  99. package/corpus/templates/slides/changelog/2026-08-11-deck-titles-now-use-slide-content-instead-of-placeholder-filenames.md +6 -0
  100. package/corpus/templates/slides/changelog/2026-08-11-history-now-opens-from-the-deck-overflow-menu.md +6 -0
  101. package/corpus/templates/slides/changelog/2026-08-11-images-can-be-dropped-directly-onto-slides-and-resized-or-re.md +6 -0
  102. package/corpus/templates/slides/changelog/2026-08-11-new-decks-now-transition-directly-to-a-full-page-editor-load.md +6 -0
  103. package/corpus/templates/slides/changelog/2026-08-11-selected-slide-layers-can-be-nudged-precisely-with-arrow-key.md +6 -0
  104. package/corpus/templates/slides/changelog/2026-08-11-slides-can-copy-and-paste-element-styles-with-cmd-option-c-v.md +6 -0
  105. package/corpus/templates/slides/changelog/2026-08-11-slides-shows-one-compact-ai-marker-and-keeps-your-current-sl.md +6 -0
  106. package/corpus/templates/slides/changelog/2026-08-11-text-edits-preserve-slide-layout-when-no-changes-are-made.md +6 -0
  107. package/corpus/templates/slides/changelog/2026-08-11-the-style-toolbar-now-uses-the-full-available-width.md +6 -0
  108. package/corpus/templates/slides/changelog/2026-08-11-undo-and-redo-now-move-one-visible-slide-state-at-a-time.md +6 -0
  109. package/corpus/templates/slides/server/lib/chat-attachments.ts +6 -2
  110. package/corpus/templates/slides/shared/deck-title.ts +56 -0
  111. package/corpus/templates/tasks/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  112. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +5 -0
  113. package/dist/agent/run-store.js +6 -1
  114. package/dist/agent/thread-data-builder.js +19 -44
  115. package/dist/agent/types.d.ts +13 -0
  116. package/dist/catalog.json +1 -0
  117. package/dist/cli/create.js +1 -1
  118. package/dist/client/AssistantChat.js +58 -10
  119. package/dist/client/FileStorageSetupCard.d.ts +6 -0
  120. package/dist/client/FileStorageSetupCard.js +18 -0
  121. package/dist/client/NewWorkspaceAppFlow.js +10 -0
  122. package/dist/client/agent-chat-adapter.js +87 -11
  123. package/dist/client/agent-chat.d.ts +1 -1
  124. package/dist/client/agent-chat.js +8 -3
  125. package/dist/client/chat/attachment-adapters.js +10 -0
  126. package/dist/client/chat/message-components.d.ts +4 -1
  127. package/dist/client/chat/message-components.js +37 -13
  128. package/dist/client/chat/tool-call-display.js +14 -0
  129. package/dist/client/chat-first/apps-rail.d.ts +1 -1
  130. package/dist/client/chat-first/apps-rail.js +6 -6
  131. package/dist/client/chat-first/copy.js +1 -0
  132. package/dist/client/chat-first/types.d.ts +1 -0
  133. package/dist/client/extensions/EmbeddedExtension.js +1 -1
  134. package/dist/client/extensions/ExtensionViewer.js +1 -1
  135. package/dist/client/extensions/InlineExtensionFrame.js +1 -1
  136. package/dist/client/onboarding/FirstRunOnboarding.js +17 -2
  137. package/dist/client/settings/FileStorageSettingsForm.d.ts +2 -0
  138. package/dist/client/settings/FileStorageSettingsForm.js +113 -0
  139. package/dist/client/settings/SettingsPanel.js +2 -1
  140. package/dist/client/sse-event-processor.js +21 -0
  141. package/dist/extensions/actions.js +2 -2
  142. package/dist/extensions/html-shell.js +3 -1
  143. package/dist/file-upload/pre-upload-attachments.d.ts +15 -10
  144. package/dist/file-upload/pre-upload-attachments.js +68 -24
  145. package/dist/file-upload/registry.d.ts +4 -5
  146. package/dist/file-upload/registry.js +7 -14
  147. package/dist/file-upload/s3.d.ts +9 -0
  148. package/dist/file-upload/s3.js +168 -0
  149. package/dist/file-upload/types.d.ts +3 -3
  150. package/dist/file-upload/types.js +2 -2
  151. package/dist/onboarding/app-profile.js +23 -1
  152. package/dist/onboarding/default-steps.js +69 -0
  153. package/dist/onboarding/types.d.ts +2 -0
  154. package/dist/progress/routes.d.ts +1 -1
  155. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  156. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  157. package/dist/resources/handlers.d.ts +1 -1
  158. package/dist/secrets/routes.d.ts +6 -6
  159. package/dist/server/agent-chat/browser-team-tools.js +20 -0
  160. package/dist/server/agent-chat-plugin.js +12 -3
  161. package/dist/server/auth.js +45 -21
  162. package/dist/server/better-auth-instance.d.ts +2 -0
  163. package/dist/server/better-auth-instance.js +2 -2
  164. package/dist/server/core-routes-plugin.d.ts +1 -0
  165. package/dist/server/core-routes-plugin.js +26 -16
  166. package/dist/server/identity-sso-store.d.ts +3 -0
  167. package/dist/server/identity-sso-store.js +45 -0
  168. package/dist/server/identity-sso.d.ts +20 -10
  169. package/dist/server/identity-sso.js +79 -31
  170. package/dist/server/index.d.ts +1 -1
  171. package/dist/server/index.js +1 -1
  172. package/dist/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  173. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +5 -0
  174. package/dist/templates/default/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  175. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +5 -0
  176. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +24 -1
  177. package/dist/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  178. package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -2
  179. package/dist/templates/workspace-core/.agents/skills/extensions/references/api.md +1 -1
  180. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +5 -0
  181. package/dist/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +3 -1
  182. package/docs/content/authentication.mdx +27 -0
  183. package/docs/content/cross-app-sso.mdx +38 -8
  184. package/docs/content/extensions.mdx +15 -15
  185. package/docs/content/file-uploads.mdx +10 -12
  186. package/docs/content/generative-ui.mdx +6 -1
  187. package/package.json +2 -2
  188. package/src/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  189. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +5 -0
  190. package/src/templates/default/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  191. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +5 -0
  192. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +24 -1
  193. package/src/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  194. package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -2
  195. package/src/templates/workspace-core/.agents/skills/extensions/references/api.md +1 -1
  196. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +5 -0
  197. package/src/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +3 -1
@@ -144,7 +144,7 @@ pub(crate) mod macos {
144
144
  kAudioObjectPropertyElementMain, kAudioObjectPropertyScopeGlobal, kAudioObjectSystemObject,
145
145
  AudioObjectGetPropertyData, AudioObjectID, AudioObjectPropertyAddress,
146
146
  };
147
- use objc2_foundation::{NSArray, NSError, NSLocale, NSString};
147
+ use objc2_foundation::{NSArray, NSBundle, NSError, NSLocale, NSString};
148
148
  use objc2_speech::{
149
149
  SFSpeechAudioBufferRecognitionRequest, SFSpeechRecognitionResult, SFSpeechRecognitionTask,
150
150
  SFSpeechRecognizer, SFSpeechRecognizerAuthorizationStatus,
@@ -154,6 +154,29 @@ pub(crate) mod macos {
154
154
 
155
155
  use screencapturekit::audio_devices::AudioInputDevice;
156
156
 
157
+ const SPEECH_USAGE_DESCRIPTION_KEY: &str = "NSSpeechRecognitionUsageDescription";
158
+ const SPEECH_USAGE_DESCRIPTION_ERROR: &str =
159
+ "Clips cannot start macOS speech recognition because the app bundle is missing NSSpeechRecognitionUsageDescription.";
160
+
161
+ pub(crate) fn has_speech_usage_description() -> bool {
162
+ let bundle = NSBundle::mainBundle();
163
+ let Some(info) = bundle.infoDictionary() else {
164
+ return false;
165
+ };
166
+ let key = NSString::from_str(SPEECH_USAGE_DESCRIPTION_KEY);
167
+ info.objectForKey(&*key)
168
+ .and_then(|value| value.downcast::<NSString>().ok())
169
+ .is_some_and(|value| !value.is_empty())
170
+ }
171
+
172
+ fn ensure_speech_usage_description() -> Result<(), String> {
173
+ if has_speech_usage_description() {
174
+ Ok(())
175
+ } else {
176
+ Err(SPEECH_USAGE_DESCRIPTION_ERROR.into())
177
+ }
178
+ }
179
+
157
180
  /// Who owns an in-flight `SpeechSession`. Meetings fall back to this
158
181
  /// mic-only engine when whisper fails; a Fn/dictation press must not be
159
182
  /// able to silently kill a meeting's live capture (D10). Priority rule:
@@ -416,6 +439,11 @@ pub(crate) mod macos {
416
439
  /// system has an answer. The handler itself only sends a value on a
417
440
  /// channel; no ObjC interop, no UI work.
418
441
  fn ensure_authorized() -> Result<(), String> {
442
+ // macOS aborts the process, rather than returning an error, when
443
+ // Speech.framework is touched without this usage description. This
444
+ // guard keeps `tauri dev` and malformed bundles recoverable.
445
+ ensure_speech_usage_description()?;
446
+
419
447
  // Fast path: already known.
420
448
  let current = unsafe { SFSpeechRecognizer::authorizationStatus() };
421
449
  if current == SFSpeechRecognizerAuthorizationStatus::Authorized {
@@ -1548,6 +1576,20 @@ pub(crate) mod macos {
1548
1576
  Ok(())
1549
1577
  }
1550
1578
 
1579
+ pub fn shutdown() {
1580
+ let session = match session_slot().lock() {
1581
+ Ok(mut slot) => slot.take(),
1582
+ Err(poisoned) => poisoned.into_inner().take(),
1583
+ };
1584
+ if let Some(session) = session {
1585
+ session.cancelled.store(true, Ordering::SeqCst);
1586
+ session.stopped.store(true, Ordering::SeqCst);
1587
+ unsafe { session.task.cancel() };
1588
+ stop_engine_and_remove_tap(&session);
1589
+ }
1590
+ clear_warmed_raw_mic_engine();
1591
+ }
1592
+
1551
1593
  #[cfg(test)]
1552
1594
  mod tests {
1553
1595
  use super::{native_speech_voice_processing_mode, MicVoiceProcessingMode, SessionOwner};
@@ -1569,3 +1611,8 @@ pub(crate) mod macos {
1569
1611
  }
1570
1612
  }
1571
1613
  }
1614
+
1615
+ pub fn shutdown() {
1616
+ #[cfg(target_os = "macos")]
1617
+ macos::shutdown();
1618
+ }
@@ -75,6 +75,9 @@ mod macos {
75
75
  }
76
76
 
77
77
  fn check_speech() -> bool {
78
+ if !crate::native_speech::macos::has_speech_usage_description() {
79
+ return false;
80
+ }
78
81
  unsafe {
79
82
  SFSpeechRecognizer::authorizationStatus()
80
83
  == SFSpeechRecognizerAuthorizationStatus::Authorized
@@ -123,6 +123,11 @@ pub async fn whisper_transcription_stop(app: AppHandle) -> Result<(), String> {
123
123
  }
124
124
  }
125
125
 
126
+ pub fn shutdown(app: &AppHandle) {
127
+ #[cfg(target_os = "macos")]
128
+ macos::stop(app);
129
+ }
130
+
126
131
  #[tauri::command]
127
132
  pub async fn whisper_transcription_reset_timeline() -> Result<(), String> {
128
133
  #[cfg(target_os = "macos")]
@@ -5,6 +5,9 @@ import { PRERENDERED_PUBLIC_PAGE_PATHS } from "../../shared/prerendered-public-p
5
5
  // Clips has public share pages, embeds, and view-event tracking that must
6
6
  // reach unauthenticated viewers. Everything else sits behind auth.
7
7
  export default createAuthPlugin({
8
+ // Keep the native tray token and the Better Auth cookie useful between
9
+ // infrequent recording sessions without making them permanent.
10
+ maxAge: 60 * 60 * 24 * 90,
8
11
  // Clips owns `/_agent-native/google/*` so the same registered Google
9
12
  // callback can handle both normal sign-in and the Calendar connect flow.
10
13
  mountGoogleOAuthRoutes: false,
@@ -49,6 +49,34 @@ sendToAgentChat({
49
49
  });
50
50
  ```
51
51
 
52
+ ### Keep user text separate from injected context
53
+
54
+ Treat the visible `message` as the user's request or the shortest clear
55
+ description of an app-initiated operation. Put context the user did not type —
56
+ IDs, URLs, filenames, current selection or screen state, serialized records,
57
+ upload instructions, and bounded source excerpts — in the `context` field.
58
+ `context` is model input carried through the agent turn; it is stripped from
59
+ the rendered user message, so do not concatenate it into `message` with labels,
60
+ blank-line sections, or `<context>` tags yourself.
61
+
62
+ Use the related surface for each kind of supporting input:
63
+
64
+ | Surface | Use for |
65
+ | --- | --- |
66
+ | `message` | User-authored intent or a concise app action description |
67
+ | `context` | Derived metadata and bounded text needed to carry out that intent |
68
+ | `setAgentChatContextItem` | Context staged for a later user-submitted prompt; keep it keyed so updates replace stale context |
69
+ | `images`, `referenceImagePaths`, attachments | Binary or visual inputs; describe only the handling instructions in `context` |
70
+
71
+ If an app builds a prompt from a form, selection, upload, or editor state, keep
72
+ the visible message short and pass the assembled details as `context`. Preserve
73
+ the user's actual freeform text in `message` when it is the request; do not
74
+ restate it as metadata in `context` unless the agent needs a structured copy.
75
+
76
+ This boundary applies to auto-submitted turns and prefills. A hidden context
77
+ field is not a license to send unbounded records or secrets: cap excerpts,
78
+ prefer stable IDs and URLs, and use an action or resource lookup for full data.
79
+
52
80
  **From the UI, in the background:**
53
81
 
54
82
  ```ts
@@ -168,6 +168,11 @@ the useful information remains dense and scannable.
168
168
  - Do not build custom dropdowns, menus, popovers, modals, or confirmations with manual absolute positioning and click-outside effects.
169
169
  - Never use browser dialogs (`window.alert`, `window.confirm`, `window.prompt`). Use `AlertDialog`, `Dialog`, or app-specific confirmation UI.
170
170
  - Use Tabler icons for all first-party UI icons. Do not add Lucide, Heroicons, inline SVG icon sets, or emoji icons.
171
+ - Never guess a Tabler icon name. Names like `IconPartyPopper` or `IconConfettiCannon` feel
172
+ plausible but don't exist and crash Vite with a "Named export not found" error. Before importing
173
+ an icon you haven't used elsewhere in this app, confirm it exists by grepping
174
+ `node_modules/@tabler/icons-react/dist/tabler-icons-react.d.ts` (or the package's icon list)
175
+ for the exact name, and pick the closest real match if your first guess isn't there.
171
176
  - Keep inline help/info glyphs next to labels at `size-3` (12px) or smaller than the adjacent text. Preserve a larger hit area on the trigger, not the glyph. Use `guard:allow-large-help-icon` only for deliberate heading documentation or menu action exceptions.
172
177
  - Use `useActionQuery` and `useActionMutation` from `@agent-native/core/client` for action-backed UI. Standard CRUD should go through actions, not custom `/api/` routes.
173
178
  - Keep UI optimistic where possible: update cache and navigation immediately, then reconcile or roll back on mutation result.
@@ -0,0 +1,127 @@
1
+ import { accessFilter } from "@agent-native/core/sharing";
2
+ import {
3
+ and,
4
+ eq,
5
+ exists,
6
+ isNotNull,
7
+ isNull,
8
+ notExists,
9
+ or,
10
+ type SQL,
11
+ } from "drizzle-orm";
12
+
13
+ import { getDb, schema } from "../server/db/index.js";
14
+ import { documentDiscoveryFilter } from "../server/lib/documents.js";
15
+
16
+ export const DOCUMENT_DISCOVERY_MAX_LIMIT = 200;
17
+ export const DOCUMENT_DISCOVERY_DEFAULT_LIMIT = 50;
18
+
19
+ export type DocumentDiscoveryType = "page" | "database";
20
+
21
+ export interface DocumentDiscoveryFilters {
22
+ userEmail: string | null | undefined;
23
+ authorizedOrgIds: string[];
24
+ exactTitle?: string;
25
+ parentId?: string | null;
26
+ spaceId?: string;
27
+ documentType?: DocumentDiscoveryType;
28
+ additional?: SQL;
29
+ }
30
+
31
+ export function documentDiscoveryWhere({
32
+ userEmail,
33
+ authorizedOrgIds,
34
+ exactTitle,
35
+ parentId,
36
+ spaceId,
37
+ documentType,
38
+ additional,
39
+ }: DocumentDiscoveryFilters) {
40
+ const db = getDb();
41
+ const accessContexts = [
42
+ { userEmail: userEmail ?? undefined },
43
+ ...authorizedOrgIds.map((orgId) => ({
44
+ userEmail: userEmail ?? undefined,
45
+ orgId,
46
+ })),
47
+ ];
48
+ const activeDatabaseDocument = db
49
+ .select({ id: schema.contentDatabases.id })
50
+ .from(schema.contentDatabases)
51
+ .where(
52
+ and(
53
+ eq(schema.contentDatabases.documentId, schema.documents.id),
54
+ isNull(schema.contentDatabases.deletedAt),
55
+ ),
56
+ );
57
+ const deletedDatabaseDocument = db
58
+ .select({ id: schema.contentDatabases.id })
59
+ .from(schema.contentDatabases)
60
+ .where(
61
+ and(
62
+ eq(schema.contentDatabases.documentId, schema.documents.id),
63
+ isNotNull(schema.contentDatabases.deletedAt),
64
+ ),
65
+ );
66
+ const deletedDatabaseMembership = db
67
+ .select({ id: schema.contentDatabaseItems.id })
68
+ .from(schema.contentDatabaseItems)
69
+ .innerJoin(
70
+ schema.contentDatabases,
71
+ eq(schema.contentDatabases.id, schema.contentDatabaseItems.databaseId),
72
+ )
73
+ .where(
74
+ and(
75
+ eq(schema.contentDatabaseItems.documentId, schema.documents.id),
76
+ isNotNull(schema.contentDatabases.deletedAt),
77
+ ),
78
+ );
79
+
80
+ return and(
81
+ or(
82
+ ...accessContexts.map((context) =>
83
+ accessFilter(schema.documents, schema.documentShares, context),
84
+ ),
85
+ ),
86
+ isNull(schema.documents.trashedAt),
87
+ documentDiscoveryFilter({
88
+ userEmail,
89
+ orgIds: authorizedOrgIds,
90
+ }),
91
+ notExists(deletedDatabaseDocument),
92
+ notExists(deletedDatabaseMembership),
93
+ exactTitle === undefined
94
+ ? undefined
95
+ : eq(schema.documents.title, exactTitle),
96
+ parentId === undefined
97
+ ? undefined
98
+ : parentId === null
99
+ ? isNull(schema.documents.parentId)
100
+ : eq(schema.documents.parentId, parentId),
101
+ spaceId === undefined ? undefined : eq(schema.documents.spaceId, spaceId),
102
+ documentType === "database"
103
+ ? exists(activeDatabaseDocument)
104
+ : documentType === "page"
105
+ ? notExists(activeDatabaseDocument)
106
+ : undefined,
107
+ additional,
108
+ );
109
+ }
110
+
111
+ export function documentDiscoveryPagination(args: {
112
+ offset: number;
113
+ limit: number;
114
+ totalItems: number;
115
+ returnedItems: number;
116
+ }) {
117
+ const nextOffset = args.offset + args.returnedItems;
118
+ const hasMore = nextOffset < args.totalItems;
119
+ return {
120
+ offset: args.offset,
121
+ limit: args.limit,
122
+ totalItems: args.totalItems,
123
+ returnedItems: args.returnedItems,
124
+ hasMore,
125
+ nextOffset: hasMore ? nextOffset : null,
126
+ };
127
+ }