@agent-native/core 0.164.4 → 0.164.12

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 (268) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +84 -0
  3. package/corpus/templates/calendar/AGENTS.md +5 -0
  4. package/corpus/templates/calendar/actions/delete-event.ts +1 -1
  5. package/corpus/templates/calendar/actions/delete-events.ts +508 -0
  6. package/corpus/templates/calendar/actions/event-action-helpers.ts +1 -1
  7. package/corpus/templates/calendar/actions/list-events.ts +53 -0
  8. package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +36 -47
  9. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +3 -0
  10. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +18 -1
  11. package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +256 -38
  12. package/corpus/templates/calendar/app/hooks/use-google-auth.ts +43 -3
  13. package/corpus/templates/calendar/app/i18n-data.ts +120 -0
  14. package/corpus/templates/calendar/app/lib/event-form-utils.ts +113 -0
  15. package/corpus/templates/calendar/changelog/2026-08-18-the-agent-can-now-remove-many-meetings-at-once-such-as-every.md +6 -0
  16. package/corpus/templates/calendar/server/handlers/google-auth.ts +119 -3
  17. package/corpus/templates/calendar/server/lib/event-weekday.ts +112 -0
  18. package/corpus/templates/calendar/server/routes/[base]/_agent-native/google/add-account/auth-url.post.ts +1 -0
  19. package/corpus/templates/calendar/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
  20. package/corpus/templates/calendar/server/routes/_agent-native/google/add-account/auth-url.post.ts +1 -0
  21. package/corpus/templates/calendar/server/routes/_agent-native/google/auth-url.post.ts +1 -0
  22. package/corpus/templates/clips/app/components/player/video-player.tsx +12 -0
  23. package/corpus/templates/clips/app/routes/_app.dictate.tsx +18 -4
  24. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +114 -20
  25. package/corpus/templates/clips/app/routes/share.$shareId.tsx +83 -78
  26. package/corpus/templates/clips/changelog/2026-08-19-clips-nightly-builds-are-available-for-trying-the-latest-upd.md +6 -0
  27. package/corpus/templates/clips/changelog/2026-08-19-social-preview-video-frames.md +1 -0
  28. package/corpus/templates/clips/desktop/README.md +19 -4
  29. package/corpus/templates/clips/desktop/src/app.tsx +78 -16
  30. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +3 -2
  31. package/corpus/templates/clips/desktop/src-tauri/tauri.conf.json +1 -0
  32. package/corpus/templates/clips/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
  33. package/corpus/templates/clips/server/routes/_agent-native/google/auth-url.get.ts +41 -3
  34. package/corpus/templates/clips/server/routes/_agent-native/google/auth-url.post.ts +1 -0
  35. package/corpus/templates/clips/server/routes/_agent-native/google/callback.get.ts +22 -1
  36. package/corpus/templates/clips/server/routes/api/clips-updater.json.get.ts +41 -22
  37. package/corpus/templates/factory/AGENTS.md +3 -2
  38. package/corpus/templates/factory/actions/get-factory-graph-version.ts +69 -0
  39. package/corpus/templates/factory/actions/list-factory-automations.ts +4 -0
  40. package/corpus/templates/factory/actions/list-factory-graph-versions.ts +82 -0
  41. package/corpus/templates/factory/actions/restore-factory-graph-version.ts +163 -0
  42. package/corpus/templates/factory/actions/save-factory-graph.ts +93 -60
  43. package/corpus/templates/factory/actions/start-builder-for-item.ts +19 -17
  44. package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +100 -36
  45. package/corpus/templates/factory/app/components/factory/FactoryHistoryView.tsx +709 -0
  46. package/corpus/templates/factory/app/components/factory/FactoryInspector.tsx +62 -2
  47. package/corpus/templates/factory/app/components/ui/alert-dialog.tsx +1 -0
  48. package/corpus/templates/factory/app/i18n/en-US.ts +47 -0
  49. package/corpus/templates/factory/app/routes/factory.tsx +324 -61
  50. package/corpus/templates/factory/server/connectors/slack.ts +44 -2
  51. package/corpus/templates/factory/server/plugins/agent-chat.ts +12 -8
  52. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +13 -0
  53. package/corpus/templates/factory/server/triage/slack-client.ts +5 -0
  54. package/corpus/templates/factory/server/triage/slack-poller.ts +72 -4
  55. package/corpus/templates/mail/actions/view-screen.ts +1 -1
  56. package/corpus/templates/mail/app/components/GoogleConnectBanner.tsx +100 -18
  57. package/corpus/templates/mail/server/handlers/google-auth.ts +125 -24
  58. package/corpus/templates/mail/server/lib/gmail-query.ts +5 -2
  59. package/corpus/templates/mail/server/routes/[base]/_agent-native/google/add-account/auth-url.post.ts +1 -0
  60. package/corpus/templates/mail/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
  61. package/corpus/templates/mail/server/routes/_agent-native/google/add-account/auth-url.post.ts +1 -0
  62. package/corpus/templates/mail/server/routes/_agent-native/google/auth-url.post.ts +1 -0
  63. package/corpus/templates/plan/app/pages/PlansPage.tsx +0 -12
  64. package/corpus/templates/plan/changelog/2026-08-19-plan-prompts-now-guide-you-to-connect-ai-before-sending-and-.md +6 -0
  65. package/corpus/templates/slides/actions/approve-deck-access-request.ts +234 -0
  66. package/corpus/templates/slides/actions/get-deck-access-status.ts +35 -2
  67. package/corpus/templates/slides/actions/request-deck-access.ts +263 -64
  68. package/corpus/templates/slides/app/components/editor/MissingDeckAccessPane.tsx +196 -0
  69. package/corpus/templates/slides/app/components/editor/RequestDeckAccessDialog.tsx +101 -0
  70. package/corpus/templates/slides/app/hooks/use-deck-access.ts +7 -4
  71. package/corpus/templates/slides/app/i18n/en-US.ts +29 -6
  72. package/corpus/templates/slides/app/pages/DeckEditor.tsx +147 -169
  73. package/corpus/templates/slides/app/routes/access-request.approve.tsx +250 -0
  74. package/corpus/templates/slides/server/db/schema.ts +8 -0
  75. package/corpus/templates/slides/server/lib/access-request-email.ts +36 -0
  76. package/corpus/templates/slides/server/lib/emails.ts +26 -0
  77. package/corpus/templates/slides/server/plugins/agent-chat.ts +7 -0
  78. package/corpus/templates/slides/server/plugins/db.ts +50 -0
  79. package/corpus/templates/slides/shared/deck-access.ts +31 -0
  80. package/dist/a2a/task-store.d.ts +1 -0
  81. package/dist/a2a/task-store.js +1 -1
  82. package/dist/agent/observational-memory/store.d.ts +1 -0
  83. package/dist/agent/observational-memory/store.js +1 -1
  84. package/dist/agent/run-store.d.ts +1 -0
  85. package/dist/agent/run-store.js +1 -1
  86. package/dist/agent/tool-approval-store.d.ts +1 -0
  87. package/dist/agent/tool-approval-store.js +1 -1
  88. package/dist/application-state/store.d.ts +1 -0
  89. package/dist/application-state/store.js +1 -1
  90. package/dist/browser-sessions/store.d.ts +1 -0
  91. package/dist/browser-sessions/store.js +1 -1
  92. package/dist/checkpoints/store.d.ts +1 -0
  93. package/dist/checkpoints/store.js +1 -1
  94. package/dist/cli/code-agent-schedules.d.ts +11 -0
  95. package/dist/cli/code-agent-schedules.js +44 -5
  96. package/dist/client/AgentPanel.d.ts +5 -1
  97. package/dist/client/AgentPanel.js +2 -2
  98. package/dist/client/AssistantChat.d.ts +5 -1
  99. package/dist/client/AssistantChat.js +40 -6
  100. package/dist/client/EnvironmentBadge.d.ts +7 -13
  101. package/dist/client/EnvironmentBadge.js +43 -92
  102. package/dist/client/agent-chat/index.d.ts +1 -0
  103. package/dist/client/agent-chat/index.js +1 -0
  104. package/dist/client/agent-chat-adapter.js +76 -50
  105. package/dist/client/app-providers.js +4 -4
  106. package/dist/client/chat/message-components.d.ts +4 -0
  107. package/dist/client/chat/message-components.js +28 -1
  108. package/dist/client/chat/run-recovery.js +26 -0
  109. package/dist/client/chat-first/apps-rail.d.ts +1 -0
  110. package/dist/client/chat-first/apps-rail.js +28 -2
  111. package/dist/client/sse-event-processor.js +13 -8
  112. package/dist/client/use-active-agent-chat-run.d.ts +2 -0
  113. package/dist/client/use-active-agent-chat-run.js +22 -0
  114. package/dist/coding-tools/sandbox/executions-store.d.ts +1 -0
  115. package/dist/coding-tools/sandbox/executions-store.js +1 -1
  116. package/dist/collab/awareness-store.d.ts +1 -0
  117. package/dist/collab/awareness-store.js +1 -1
  118. package/dist/collab/routes.d.ts +1 -1
  119. package/dist/collab/storage.d.ts +1 -0
  120. package/dist/collab/storage.js +1 -1
  121. package/dist/collab/struct-routes.d.ts +1 -1
  122. package/dist/db/client.js +3 -3
  123. package/dist/db/ddl-guard.js +8 -0
  124. package/dist/db/migration-runtime.d.ts +23 -0
  125. package/dist/db/migration-runtime.js +41 -0
  126. package/dist/db/migrations.d.ts +1 -2
  127. package/dist/db/migrations.js +2 -24
  128. package/dist/email-catalog/log.d.ts +1 -0
  129. package/dist/email-catalog/log.js +1 -1
  130. package/dist/guards/index.d.ts +1 -0
  131. package/dist/guards/index.js +1 -0
  132. package/dist/guards/release-schema-complete.d.ts +22 -0
  133. package/dist/guards/release-schema-complete.js +144 -0
  134. package/dist/integrations/a2a-continuations-store.d.ts +1 -0
  135. package/dist/integrations/a2a-continuations-store.js +1 -1
  136. package/dist/integrations/awaiting-input-store.d.ts +1 -0
  137. package/dist/integrations/awaiting-input-store.js +1 -1
  138. package/dist/integrations/config-store.d.ts +1 -0
  139. package/dist/integrations/config-store.js +1 -1
  140. package/dist/integrations/controls-store.d.ts +1 -0
  141. package/dist/integrations/controls-store.js +1 -1
  142. package/dist/integrations/identity-links-store.d.ts +1 -0
  143. package/dist/integrations/identity-links-store.js +1 -1
  144. package/dist/integrations/installations-store.d.ts +1 -0
  145. package/dist/integrations/installations-store.js +1 -1
  146. package/dist/integrations/remote-commands-store.d.ts +1 -0
  147. package/dist/integrations/remote-commands-store.js +1 -1
  148. package/dist/integrations/remote-devices-store.d.ts +1 -0
  149. package/dist/integrations/remote-devices-store.js +1 -1
  150. package/dist/integrations/remote-push-store.d.ts +1 -0
  151. package/dist/integrations/remote-push-store.js +1 -1
  152. package/dist/integrations/remote-run-events-store.d.ts +1 -0
  153. package/dist/integrations/remote-run-events-store.js +1 -1
  154. package/dist/integrations/scope-store.d.ts +1 -0
  155. package/dist/integrations/scope-store.js +1 -1
  156. package/dist/integrations/thread-mapping-store.d.ts +1 -0
  157. package/dist/integrations/thread-mapping-store.js +1 -1
  158. package/dist/integrations/usage-budget-store.d.ts +1 -0
  159. package/dist/integrations/usage-budget-store.js +1 -1
  160. package/dist/jobs/background-automation-runner.js +111 -9
  161. package/dist/jobs/run-history.d.ts +1 -0
  162. package/dist/jobs/run-history.js +1 -1
  163. package/dist/jobs/scheduler-health.d.ts +1 -0
  164. package/dist/jobs/scheduler-health.js +1 -1
  165. package/dist/mcp/approval-store.d.ts +1 -0
  166. package/dist/mcp/approval-store.js +1 -1
  167. package/dist/mcp/connect-store.d.ts +1 -0
  168. package/dist/mcp/connect-store.js +1 -1
  169. package/dist/mcp/oauth-store.d.ts +1 -0
  170. package/dist/mcp/oauth-store.js +1 -1
  171. package/dist/notifications/store.d.ts +1 -0
  172. package/dist/notifications/store.js +1 -1
  173. package/dist/oauth-tokens/store.d.ts +1 -0
  174. package/dist/oauth-tokens/store.js +1 -1
  175. package/dist/observability/routes.d.ts +5 -5
  176. package/dist/progress/routes.d.ts +1 -1
  177. package/dist/progress/store.d.ts +1 -0
  178. package/dist/progress/store.js +1 -1
  179. package/dist/provider-api/actions/custom-provider-registration.d.ts +5 -5
  180. package/dist/provider-api/actions/provider-api.d.ts +4 -4
  181. package/dist/provider-api/corpus-jobs-store.d.ts +1 -0
  182. package/dist/provider-api/corpus-jobs-store.js +1 -1
  183. package/dist/provider-api/custom-registry.d.ts +1 -0
  184. package/dist/provider-api/custom-registry.js +1 -1
  185. package/dist/provider-api/quota-governor.d.ts +1 -0
  186. package/dist/provider-api/quota-governor.js +1 -1
  187. package/dist/provider-api/staged-datasets-store.d.ts +1 -0
  188. package/dist/provider-api/staged-datasets-store.js +1 -1
  189. package/dist/resources/handlers.d.ts +3 -3
  190. package/dist/resources/store.d.ts +1 -0
  191. package/dist/resources/store.js +1 -1
  192. package/dist/scripts/dev/index.js +15 -1
  193. package/dist/search/index.js +3 -0
  194. package/dist/secrets/routes.d.ts +9 -9
  195. package/dist/secrets/storage.d.ts +1 -0
  196. package/dist/secrets/storage.js +1 -1
  197. package/dist/server/agent-chat-plugin.js +38 -9
  198. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  199. package/dist/server/agent-teams-run-queue.d.ts +1 -0
  200. package/dist/server/agent-teams-run-queue.js +1 -1
  201. package/dist/server/auth-marketing.js +3 -1
  202. package/dist/server/auth.d.ts +18 -1
  203. package/dist/server/auth.js +370 -154
  204. package/dist/server/embed-session.d.ts +1 -0
  205. package/dist/server/embed-session.js +1 -1
  206. package/dist/server/google-auth-plugin.js +3 -1
  207. package/dist/server/google-oauth.d.ts +10 -0
  208. package/dist/server/google-oauth.js +25 -1
  209. package/dist/server/identity-sso-store.d.ts +1 -0
  210. package/dist/server/identity-sso-store.js +1 -1
  211. package/dist/server/index.d.ts +1 -1
  212. package/dist/server/index.js +1 -1
  213. package/dist/server/onboarding-html.js +200 -31
  214. package/dist/server/poll.d.ts +2 -0
  215. package/dist/server/poll.js +19 -1
  216. package/dist/server/realtime-token.d.ts +1 -1
  217. package/dist/server/release-migrations.js +5 -0
  218. package/dist/server/release-schema.d.ts +46 -0
  219. package/dist/server/release-schema.js +307 -0
  220. package/dist/settings/store.d.ts +1 -0
  221. package/dist/settings/store.js +1 -1
  222. package/dist/shared/environment-lanes.d.ts +28 -0
  223. package/dist/shared/environment-lanes.js +61 -0
  224. package/dist/sharing/actions/share-resource.js +22 -2
  225. package/dist/styles/agent-native.css +17 -15
  226. package/dist/triggers/routes.d.ts +11 -0
  227. package/dist/triggers/routes.js +10 -0
  228. package/dist/usage/alerts-store.d.ts +1 -0
  229. package/dist/usage/alerts-store.js +1 -1
  230. package/dist/usage/store.d.ts +1 -0
  231. package/dist/usage/store.js +1 -1
  232. package/docs/content/actions-access-control.mdx +136 -2
  233. package/docs/content/actions-agent-tools.mdx +101 -0
  234. package/docs/content/actions-defining.mdx +2 -2
  235. package/docs/content/audit-log.mdx +1 -1
  236. package/docs/content/authentication.mdx +1 -1
  237. package/docs/content/cloudflare.mdx +43 -0
  238. package/docs/content/creating-templates.mdx +1 -1
  239. package/docs/content/deploy-an-app.mdx +109 -0
  240. package/docs/content/deployment-environment-variables.mdx +101 -0
  241. package/docs/content/deployment.mdx +125 -792
  242. package/docs/content/docs-components.mdx +2 -2
  243. package/docs/content/environment-variables.mdx +41 -41
  244. package/docs/content/file-uploads.mdx +74 -40
  245. package/docs/content/harness-agents.mdx +2 -2
  246. package/docs/content/internationalization.mdx +61 -65
  247. package/docs/content/key-concepts.mdx +3 -3
  248. package/docs/content/multi-app-workspace.mdx +2 -2
  249. package/docs/content/netlify.mdx +47 -0
  250. package/docs/content/node-docker.mdx +138 -0
  251. package/docs/content/organizations-teams-permissions.mdx +5 -5
  252. package/docs/content/other-platforms.mdx +27 -0
  253. package/docs/content/ssr-caching.mdx +45 -0
  254. package/docs/content/template-assets-developers.mdx +2 -2
  255. package/docs/content/template-chat-developers.mdx +2 -2
  256. package/docs/content/template-content-developers.mdx +2 -2
  257. package/docs/content/{local-file-mode.mdx → template-content-local-files.mdx} +2 -1
  258. package/docs/content/template-content-sync.mdx +2 -2
  259. package/docs/content/toolkit-agent-ux.mdx +1 -1
  260. package/docs/content/toolkit-resources.mdx +2 -2
  261. package/docs/content/updating-ui-in-production.mdx +35 -0
  262. package/docs/content/vercel.mdx +38 -0
  263. package/docs/content/what-is-agent-native.mdx +1 -1
  264. package/docs/content/workspace-deployment.mdx +350 -0
  265. package/package.json +2 -2
  266. package/docs/content/agents.mdx +0 -9
  267. package/docs/content/database.mdx +0 -317
  268. package/docs/content/human-approval.mdx +0 -156
@@ -57,7 +57,9 @@ current factory graph, and executes only the explicit automation prompts that
57
57
  are stored in the organization.
58
58
  Use the Factory actions as the source of truth. When a user asks to
59
59
  create or change a factory, first inspect the current graph, then propose a complete
60
- versioned graph through save-factory-graph with source=ai and a concise changeSummary.
60
+ versioned graph through save-factory-graph with source=ai, the inspected graphVersion
61
+ as expectedGraphVersion, and a concise changeSummary. Never save a graph from a
62
+ stale read: refresh and re-propose when the action reports a version conflict.
61
63
  Never hide a graph change in prose: the visual map and the saved graph must agree.
62
64
  The graph is currently a reviewable blueprint, not the runtime router: automation
63
65
  markdown resources are the runtime prompts, while enabled triage rules are evaluated
@@ -89,13 +91,15 @@ Builder, auto-approve, or auto-merge those items. Slack clear bugs use the
89
91
  thread-preserving start-builder-for-item flow; GitHub and Sentry clear bugs use
90
92
  the Builder agent-run flow. Slack repeat reports must be clustered by underlying
91
93
  symptom, with one Builder thread for the cluster and 👀 on every grouped report.
92
- Use /address-feedback for the repository feedback workflow. For pull requests,
93
- follow review-prs: read the complete diff and review evidence, verify current
94
- BuilderIO membership, preserve the ultra-scary safety gate, and distinguish
95
- unknown or unresolved checks from clean ones. Auto-merge also requires a
96
- verified Factory Builder run. When a user says to do a review-gated item now,
97
- use the explicit approval action, which records the approver and applies the
98
- rule's configured executor policy. Keep Slack replies concise and link to the
94
+ After classifying an item, call start-builder-for-item with clearBug true or
95
+ false and a short reason so a skip is recorded. Use /address-feedback for the
96
+ repository feedback workflow. For pull requests, follow review-prs: read the
97
+ complete diff and review evidence, verify current BuilderIO membership, preserve
98
+ the ultra-scary safety gate, and distinguish unknown or unresolved checks from
99
+ clean ones. Auto-merge also requires a verified Factory Builder run. When a user
100
+ says to do a review-gated item now, use the explicit approval action, which
101
+ records the approver and applies the rule's configured executor policy. Keep
102
+ Slack replies concise and link to the
99
103
  Factory item when a review is needed. The scheduled builder-io-bot PR babysitter
100
104
  posts its exact feedback-fix request through GitHub, persists a 20-minute quiet
101
105
  window, and never approves or merges.`,
@@ -169,6 +169,8 @@ const FACTORY_DEFAULT_MODEL =
169
169
  process.env.FACTORY_AUTOMATION_MODEL?.trim() || "gpt-5.6-luna";
170
170
  const FACTORY_DEFAULT_MAX_ITERATIONS = 32;
171
171
  const FACTORY_DEFAULT_MAX_RUN_INPUT_TOKENS = 1_000_000;
172
+ const SKIP_RECORD_GUARD =
173
+ "After classifying each processed item, call start-builder-for-item with clearBug true or false and a short evidence-grounded reason so the skip or dispatch is recorded.";
172
174
 
173
175
  const AUTOMATION_SEEDS: AutomationSeed[] = [
174
176
  {
@@ -224,6 +226,7 @@ the representative source, every related source, and the need to fix the
224
226
  underlying boundary across the whole cluster. Never add the reaction or tag
225
227
  Builder for owner-managed Clips, Design, or Content work, or for a non-bug
226
228
  report.
229
+ After classifying each processed item, call start-builder-for-item with clearBug true or false and a short evidence-grounded reason so the skip or dispatch is recorded.
227
230
 
228
231
  Keep each run bounded. Preserve action errors and do not claim a Builder reply,
229
232
  PR, merge, or fix unless an action returned that state.
@@ -254,6 +257,7 @@ For each eligible clear bug, call start-builder-for-item with clearBug true,
254
257
  an evidence-grounded reason, and clearErrorReport containing only the bounded
255
258
  Sentry evidence. Builder should open a PR; do not claim it did so until the
256
259
  run callback or PR observation confirms it.
260
+ After classifying each processed item, call start-builder-for-item with clearBug true or false and a short evidence-grounded reason so the skip or dispatch is recorded.
257
261
  `,
258
262
  },
259
263
  {
@@ -281,6 +285,7 @@ For each eligible item call start-builder-for-item with clearBug true,
281
285
  evidence-grounded reason, and the bounded issue body as clearErrorReport.
282
286
  Preserve failures and never report a successful Builder run without its action
283
287
  confirmation.
288
+ After classifying each processed item, call start-builder-for-item with clearBug true or false and a short evidence-grounded reason so the skip or dispatch is recorded.
284
289
  `,
285
290
  },
286
291
  {
@@ -522,6 +527,14 @@ async function ensureOrganizationAutomations(
522
527
  if (promptGuard && !repaired.includes(promptGuard)) {
523
528
  repaired = `${repaired.trimEnd()}\n\n${promptGuard}\n`;
524
529
  }
530
+ if (
531
+ (seed.name === "factory-slack-feedback" ||
532
+ seed.name === "factory-sentry-errors" ||
533
+ seed.name === "factory-github-issues") &&
534
+ !repaired.includes(SKIP_RECORD_GUARD)
535
+ ) {
536
+ repaired = `${repaired.trimEnd()}\n\n${SKIP_RECORD_GUARD}\n`;
537
+ }
525
538
  if (repaired === existing.content) return;
526
539
 
527
540
  const updated = await resourcePutIfCurrent({
@@ -4,11 +4,13 @@ import {
4
4
  addEyesReaction as writeEyesReaction,
5
5
  getThread as readThread,
6
6
  getTeamInfo as readTeamInfo,
7
+ getUserInfo as readUserInfo,
7
8
  postThreadReply as writeThreadReply,
8
9
  type ChannelHistoryResult,
9
10
  type SlackPostMessageResult,
10
11
  type SlackReactionResult,
11
12
  type SlackTeamInfo,
13
+ type SlackUserInfo,
12
14
  type SlackTokenResolver,
13
15
  type ThreadRepliesResult,
14
16
  type Workspace,
@@ -57,6 +59,9 @@ export function createSlackReader(identity: SlackReaderIdentity) {
57
59
  getTeamInfo(workspace: Workspace): Promise<SlackTeamInfo> {
58
60
  return readTeamInfo(workspace, tokenResolver);
59
61
  },
62
+ getUserInfo(workspace: Workspace, userId: string): Promise<SlackUserInfo> {
63
+ return readUserInfo(workspace, userId, tokenResolver);
64
+ },
60
65
  getThread(
61
66
  workspace: Workspace,
62
67
  channelId: string,
@@ -5,6 +5,7 @@ import { createSlackReader } from "./slack-client";
5
5
  const SLACK_HISTORY_LIMIT = 100;
6
6
  const MAX_HISTORY_PAGES = 5;
7
7
  const MAX_SUMMARY_LENGTH = 500;
8
+ export const SLACK_USER_INFO_CONCURRENCY = 4;
8
9
 
9
10
  export interface SlackPollInput {
10
11
  workspace: Workspace;
@@ -37,17 +38,75 @@ function compactText(text: string): string {
37
38
  : compact;
38
39
  }
39
40
 
40
- function messageLabel(message: SlackMessage): string {
41
+ function messageLabel(message: SlackMessage, resolvedLabel?: string): string {
41
42
  if (message.bot_id || message.username) {
42
43
  return `Slack bot ${message.username ?? message.bot_id ?? "unknown"}`;
43
44
  }
44
- return `Slack user ${message.user ?? "unknown"}`;
45
+ return `Slack user ${resolvedLabel ?? message.user ?? "unknown"}`;
46
+ }
47
+
48
+ function slackUserLabel(
49
+ user: { name: string | null; displayName: string | null } | null,
50
+ userId: string,
51
+ ): string {
52
+ const displayName = user?.displayName?.trim();
53
+ if (displayName) return displayName;
54
+ const name = user?.name?.trim();
55
+ if (name) return name.startsWith("@") ? name : `@${name}`;
56
+ return userId;
57
+ }
58
+
59
+ async function resolveUserLabels(
60
+ slack: ReturnType<typeof createSlackReader>,
61
+ workspace: Workspace,
62
+ messages: SlackMessage[],
63
+ ): Promise<Map<string, string>> {
64
+ const userIds = [
65
+ ...new Set(
66
+ messages
67
+ .filter((message) => !message.bot_id && !message.username)
68
+ .map((message) => message.user?.trim())
69
+ .filter((userId): userId is string => Boolean(userId)),
70
+ ),
71
+ ];
72
+ const labels = new Map<string, string>();
73
+ await mapWithConcurrency(
74
+ userIds,
75
+ SLACK_USER_INFO_CONCURRENCY,
76
+ async (userId) => {
77
+ try {
78
+ const user = await slack.getUserInfo(workspace, userId);
79
+ labels.set(userId, slackUserLabel(user, userId));
80
+ } catch {
81
+ labels.set(userId, userId);
82
+ }
83
+ },
84
+ );
85
+ return labels;
86
+ }
87
+
88
+ async function mapWithConcurrency<T>(
89
+ values: T[],
90
+ concurrency: number,
91
+ mapper: (value: T) => Promise<void>,
92
+ ): Promise<void> {
93
+ let nextIndex = 0;
94
+ const worker = async () => {
95
+ while (nextIndex < values.length) {
96
+ const index = nextIndex++;
97
+ await mapper(values[index]!);
98
+ }
99
+ };
100
+ await Promise.all(
101
+ Array.from({ length: Math.min(concurrency, values.length) }, worker),
102
+ );
45
103
  }
46
104
 
47
105
  function toEnvelope(
48
106
  channelId: string,
49
107
  message: SlackMessage,
50
108
  teamDomain?: string,
109
+ userLabels?: Map<string, string>,
51
110
  ): IngestionEnvelope {
52
111
  const threadTs = message.thread_ts ?? message.ts;
53
112
  const suppliedPermalink = (message as SlackMessage & { permalink?: string })
@@ -63,7 +122,10 @@ function toEnvelope(
63
122
  externalId: `${channelId}:${threadTs}`,
64
123
  receivedAt: new Date().toISOString(),
65
124
  ...(sourceUrl ? { sourceUrl } : {}),
66
- title: messageLabel(message),
125
+ title: messageLabel(
126
+ message,
127
+ message.user ? userLabels?.get(message.user) : undefined,
128
+ ),
67
129
  summary: compactText(message.text),
68
130
  channelId,
69
131
  threadTs,
@@ -149,9 +211,15 @@ export async function pollSlackChannel({
149
211
  { value: priorTs, raw: priorLastSlackTs },
150
212
  );
151
213
 
214
+ const userLabels = await resolveUserLabels(
215
+ slack,
216
+ workspace,
217
+ uniqueNewMessages.map(({ message }) => message),
218
+ );
219
+
152
220
  return {
153
221
  envelopes: uniqueNewMessages.map(({ message }) =>
154
- toEnvelope(channelId, message, teamDomain),
222
+ toEnvelope(channelId, message, teamDomain, userLabels),
155
223
  ),
156
224
  nextLastSlackTs: maxSeen.raw,
157
225
  nextHistoryCursor,
@@ -70,7 +70,7 @@ async function fetchEmailList(
70
70
 
71
71
  const gmailQuery = buildGmailEmailSearchQuery({ view, q: search });
72
72
  const effectiveQuery =
73
- view === "all" && !search ? "" : gmailQuery || "in:inbox -in:sent";
73
+ view === "all" && !search ? "" : gmailQuery || "in:inbox";
74
74
  const { messages } = await listGmailMessages(
75
75
  effectiveQuery,
76
76
  50,
@@ -76,6 +76,25 @@ const ADD_ACCOUNT_POLL_ABORT_MS = Math.max(
76
76
  ADD_ACCOUNT_POLL_INTERVAL_MS * 4,
77
77
  );
78
78
 
79
+ function newDesktopOAuthVerifier(): string | null {
80
+ const cryptoApi = globalThis.crypto;
81
+ const randomUuid = cryptoApi?.randomUUID;
82
+ if (typeof randomUuid === "function") {
83
+ return `${randomUuid.call(cryptoApi)}${randomUuid.call(cryptoApi)}`;
84
+ }
85
+ if (typeof cryptoApi?.getRandomValues === "function") {
86
+ const bytes = new Uint8Array(32);
87
+ cryptoApi.getRandomValues(bytes);
88
+ let binary = "";
89
+ for (const byte of bytes) binary += String.fromCharCode(byte);
90
+ return btoa(binary)
91
+ .replace(/\+/g, "-")
92
+ .replace(/\//g, "_")
93
+ .replace(/=+$/, "");
94
+ }
95
+ return null;
96
+ }
97
+
79
98
  interface GoogleConnectBannerProps {
80
99
  variant?: "banner" | "hero";
81
100
  }
@@ -128,20 +147,81 @@ export function GoogleConnectBanner({
128
147
  const flowId =
129
148
  crypto.randomUUID?.() ||
130
149
  Math.random().toString(36).slice(2) + Date.now().toString(36);
150
+ const verifier = newDesktopOAuthVerifier();
151
+ if (!verifier) {
152
+ setDesktopAuthIssue({
153
+ code: "desktop_auth_start_failed",
154
+ message: t("mail.error.failedToConnect"),
155
+ });
156
+ return;
157
+ }
131
158
  const origin = window.location.origin;
132
159
  const endpoint = addAccount
133
160
  ? "/_agent-native/google/add-account/auth-url"
134
161
  : "/_agent-native/google/auth-url";
135
- const redirectUri = encodeURIComponent(
136
- oauthRedirectUri("/_agent-native/google/callback"),
137
- );
138
- window.open(
139
- `${origin}${agentNativePath(endpoint)}?redirect_uri=${redirectUri}&desktop=1&flow_id=${flowId}&redirect=1`,
140
- "_blank",
141
- );
162
+ const params = new URLSearchParams({
163
+ redirect_uri: oauthRedirectUri("/_agent-native/google/callback"),
164
+ desktop: "1",
165
+ flow_id: flowId,
166
+ });
167
+ const popup = window.open("", "_blank");
168
+ if (!popup) {
169
+ setDesktopAuthIssue({
170
+ code: "popup_blocked",
171
+ message: t("mail.error.failedToConnect"),
172
+ });
173
+ return;
174
+ }
175
+ let pollHandle: ReturnType<typeof setInterval> | null = null;
176
+ const stopPoll = () => {
177
+ if (!pollHandle) return;
178
+ clearInterval(pollHandle);
179
+ if (desktopPollRef.current === pollHandle) {
180
+ desktopPollRef.current = null;
181
+ }
182
+ pollHandle = null;
183
+ };
184
+ void fetch(`${origin}${agentNativePath(endpoint)}?${params.toString()}`, {
185
+ method: "POST",
186
+ credentials: "include",
187
+ headers: { "X-Agent-Native-Desktop-Verifier": verifier },
188
+ })
189
+ .then(async (response) => {
190
+ let data: {
191
+ url?: unknown;
192
+ message?: unknown;
193
+ error?: unknown;
194
+ };
195
+ try {
196
+ data = (await response.json()) as typeof data;
197
+ } catch {
198
+ throw new Error(t("mail.googleConnect.connectionFailed"));
199
+ }
200
+ if (!response.ok || typeof data?.url !== "string") {
201
+ const message =
202
+ typeof data.message === "string"
203
+ ? data.message
204
+ : typeof data.error === "string"
205
+ ? data.error
206
+ : t("mail.googleConnect.connectionFailed");
207
+ throw new Error(message);
208
+ }
209
+ popup.location.href = data.url;
210
+ })
211
+ .catch((error) => {
212
+ stopPoll();
213
+ popup.close();
214
+ setDesktopAuthIssue({
215
+ code: "desktop_auth_start_failed",
216
+ message:
217
+ error instanceof Error
218
+ ? error.message
219
+ : t("mail.googleConnect.connectionFailed"),
220
+ });
221
+ });
142
222
  const start = Date.now();
143
223
  if (desktopPollRef.current) clearInterval(desktopPollRef.current);
144
- desktopPollRef.current = setInterval(async () => {
224
+ pollHandle = setInterval(async () => {
145
225
  if (document.hidden || desktopPollInFlightRef.current) return;
146
226
  desktopPollInFlightRef.current = true;
147
227
  const controller = new AbortController();
@@ -153,18 +233,21 @@ export function GoogleConnectBanner({
153
233
  try {
154
234
  const res = await fetch(
155
235
  agentNativePath(
156
- `/_agent-native/auth/desktop-exchange?flow_id=${flowId}`,
236
+ `/_agent-native/auth/desktop-exchange?flow_id=${encodeURIComponent(flowId)}`,
157
237
  ),
158
- { signal: controller.signal },
238
+ {
239
+ headers: {
240
+ "X-Agent-Native-Desktop-Verifier": verifier,
241
+ },
242
+ signal: controller.signal,
243
+ },
159
244
  );
160
245
  const data = await res.json();
161
246
  if (data?.error) {
162
- clearInterval(desktopPollRef.current!);
163
- desktopPollRef.current = null;
247
+ stopPoll();
164
248
  setDesktopAuthIssue(data);
165
249
  } else if (data?.token) {
166
- clearInterval(desktopPollRef.current!);
167
- desktopPollRef.current = null;
250
+ stopPoll();
168
251
  await fetch(
169
252
  agentNativePath(
170
253
  `/_agent-native/auth/session?_session=${data.token}`,
@@ -176,13 +259,11 @@ export function GoogleConnectBanner({
176
259
  );
177
260
  window.location.reload();
178
261
  } else if (Date.now() - start > 120_000) {
179
- clearInterval(desktopPollRef.current!);
180
- desktopPollRef.current = null;
262
+ stopPoll();
181
263
  }
182
264
  } catch {
183
265
  if (Date.now() - start > 120_000) {
184
- clearInterval(desktopPollRef.current!);
185
- desktopPollRef.current = null;
266
+ stopPoll();
186
267
  }
187
268
  }
188
269
  } finally {
@@ -190,6 +271,7 @@ export function GoogleConnectBanner({
190
271
  desktopPollInFlightRef.current = false;
191
272
  }
192
273
  }, DESKTOP_POLL_INTERVAL_MS);
274
+ desktopPollRef.current = pollHandle;
193
275
  }
194
276
 
195
277
  const [authError, setAuthError] = useState<string | null>(null);
@@ -15,6 +15,9 @@ import {
15
15
  oauthCallbackResponse,
16
16
  oauthDesktopExchangePage,
17
17
  oauthErrorPage,
18
+ registerDesktopExchange,
19
+ prepareDesktopOAuthBrowserBinding,
20
+ matchesDesktopOAuthBrowserBinding,
18
21
  safeReturnPath,
19
22
  setDesktopExchange,
20
23
  setDesktopExchangeError,
@@ -22,6 +25,8 @@ import {
22
25
  import { getUserSetting, putUserSetting } from "@agent-native/core/settings";
23
26
  import {
24
27
  defineEventHandler,
28
+ getHeader,
29
+ getMethod,
25
30
  getQuery,
26
31
  setResponseStatus,
27
32
  type H3Event,
@@ -64,17 +69,25 @@ function googleOAuthErrorPayload(
64
69
  error?.name === "OAuthAccountOwnedByOtherUserError"
65
70
  ) {
66
71
  const account = error.accountId || "This Google account";
72
+ const message = `${account} is connected to another login. Sign out, then sign in with ${account}.`;
67
73
  return {
68
- message: `${account} is already connected to another login. Sign out, then sign in with the login that originally connected it.`,
74
+ message,
69
75
  code: "account_owner_mismatch",
70
76
  accountId: error.accountId,
71
77
  };
72
78
  }
73
79
 
74
80
  const msg = error?.message || "Unknown error";
81
+ const statusCode = Number(error?.statusCode || error?.status || 0);
75
82
  const isPermission =
83
+ error?.oauthErrorCode === "access_denied" ||
84
+ error?.oauthErrorCode === "forbidden" ||
85
+ error?.oauthErrorCode === "insufficient_scope" ||
76
86
  msg.includes("Insufficient Permission") ||
77
- msg.includes("insufficient_scope");
87
+ msg.includes("insufficient_scope") ||
88
+ /insufficient authentication scopes/i.test(msg) ||
89
+ (statusCode === 403 &&
90
+ /\b(?:scope|permission|forbidden|access denied)\b/i.test(msg));
78
91
  return {
79
92
  message: isPermission
80
93
  ? "This account wasn't granted the required permissions. Make sure you check all the permission boxes on the consent screen. If the app is in testing mode, add this email as a test user in Google Cloud Console."
@@ -99,6 +112,7 @@ function googleOAuthErrorResponse(
99
112
  export const getGoogleAuthUrl = defineEventHandler(async (event: H3Event) => {
100
113
  try {
101
114
  const q = getQuery(event);
115
+ const method = getMethod(event);
102
116
  const redirectUri = resolveOAuthRedirectUri(event);
103
117
  if (!redirectUri) {
104
118
  setResponseStatus(event, 400);
@@ -113,6 +127,34 @@ export const getGoogleAuthUrl = defineEventHandler(async (event: H3Event) => {
113
127
  const desktop =
114
128
  isElectron(event) || q.desktop === "1" || q.desktop === "true";
115
129
  const flowId = desktop ? (q.flow_id as string) || undefined : undefined;
130
+ if (method === "POST" && (!desktop || !flowId)) {
131
+ setResponseStatus(event, 400);
132
+ return { error: "Invalid desktop exchange challenge." };
133
+ }
134
+ let desktopVerifierHash: string | undefined;
135
+ let desktopBrowserBindingHash: string | undefined;
136
+ if (flowId) {
137
+ if (method !== "POST" || q.redirect !== undefined) {
138
+ setResponseStatus(event, 400);
139
+ return { error: "Invalid desktop exchange challenge." };
140
+ }
141
+ const verifier = getHeader(event, "x-agent-native-desktop-verifier");
142
+ if (!verifier || q.verifier !== undefined) {
143
+ setResponseStatus(event, 400);
144
+ return { error: "Invalid desktop exchange challenge." };
145
+ }
146
+ try {
147
+ desktopBrowserBindingHash = prepareDesktopOAuthBrowserBinding(event);
148
+ desktopVerifierHash = await registerDesktopExchange(
149
+ flowId,
150
+ verifier,
151
+ desktopBrowserBindingHash,
152
+ );
153
+ } catch {
154
+ setResponseStatus(event, 400);
155
+ return { error: "Invalid desktop exchange challenge." };
156
+ }
157
+ }
116
158
  const requestedReturn =
117
159
  typeof q.return === "string" ? safeReturnPath(q.return) : "/";
118
160
  const returnUrl = requestedReturn !== "/" ? requestedReturn : undefined;
@@ -127,6 +169,8 @@ export const getGoogleAuthUrl = defineEventHandler(async (event: H3Event) => {
127
169
  app: OAUTH_STATE_APP_ID,
128
170
  returnUrl,
129
171
  flowId,
172
+ desktopVerifierHash,
173
+ desktopBrowserBindingHash,
130
174
  });
131
175
  const url = await getAuthUrl(undefined, redirectUri, state, owner);
132
176
  if (q.redirect === "1") {
@@ -159,22 +203,28 @@ export const handleGoogleCallback = defineEventHandler(
159
203
  );
160
204
  desktop = state.desktop ?? false;
161
205
  flowId = state.flowId;
206
+ if (
207
+ flowId &&
208
+ (!state.desktopVerifierHash ||
209
+ !state.desktopBrowserBindingHash ||
210
+ !matchesDesktopOAuthBrowserBinding(
211
+ event,
212
+ state.desktopBrowserBindingHash,
213
+ ))
214
+ ) {
215
+ throw new Error("Desktop OAuth browser binding is invalid.");
216
+ }
162
217
 
163
218
  // Handle Google authorization errors (e.g. user denied access, invalid client)
164
219
  const googleError = query.error as string | undefined;
165
220
  if (googleError) {
166
221
  const errorDesc =
167
222
  (query.error_description as string | undefined) || googleError;
168
- const isPermission =
169
- googleError === "access_denied" ||
170
- errorDesc.includes("Insufficient Permission");
171
- const userMessage = isPermission
172
- ? "Access was denied. Make sure to check all the permission boxes on the consent screen. If the app is in testing mode, add this email as a test user in Google Cloud Console."
173
- : `Connection failed: ${errorDesc}`;
174
- return googleOAuthErrorResponse(event, new Error(userMessage), {
175
- desktop,
176
- flowId,
177
- });
223
+ return googleOAuthErrorResponse(
224
+ event,
225
+ Object.assign(new Error(errorDesc), { oauthErrorCode: googleError }),
226
+ { desktop, flowId },
227
+ );
178
228
  }
179
229
 
180
230
  const code = query.code as string;
@@ -183,7 +233,13 @@ export const handleGoogleCallback = defineEventHandler(
183
233
  return { error: "Missing authorization code" };
184
234
  }
185
235
 
186
- const { redirectUri, owner: stateOwner, addAccount, returnUrl } = state;
236
+ const {
237
+ redirectUri,
238
+ owner: stateOwner,
239
+ addAccount,
240
+ returnUrl,
241
+ desktopVerifierHash,
242
+ } = state;
187
243
 
188
244
  // 1. Resolve owner (needs session context, before exchangeCode)
189
245
  const { owner, hasProductionSession } = await resolveOAuthOwner(
@@ -249,7 +305,15 @@ export const handleGoogleCallback = defineEventHandler(
249
305
  });
250
306
 
251
307
  if (flowId && sessionToken) {
252
- setDesktopExchange(flowId, sessionToken, email);
308
+ if (!desktopVerifierHash) {
309
+ throw new Error("Missing desktop exchange challenge.");
310
+ }
311
+ await setDesktopExchange(
312
+ flowId,
313
+ sessionToken,
314
+ email,
315
+ desktopVerifierHash,
316
+ );
253
317
  }
254
318
 
255
319
  // 4. Return platform-appropriate response
@@ -284,6 +348,7 @@ export const getGoogleAddAccountUrl = defineEventHandler(
284
348
  }
285
349
  try {
286
350
  const q = getQuery(event);
351
+ const method = getMethod(event);
287
352
  const redirectUri = resolveOAuthRedirectUri(event);
288
353
  if (!redirectUri) {
289
354
  setResponseStatus(event, 400);
@@ -295,6 +360,34 @@ export const getGoogleAddAccountUrl = defineEventHandler(
295
360
  const desktop =
296
361
  isElectron(event) || q.desktop === "1" || q.desktop === "true";
297
362
  const flowId = desktop ? (q.flow_id as string) || undefined : undefined;
363
+ if (method === "POST" && (!desktop || !flowId)) {
364
+ setResponseStatus(event, 400);
365
+ return { error: "Invalid desktop exchange challenge." };
366
+ }
367
+ let desktopVerifierHash: string | undefined;
368
+ let desktopBrowserBindingHash: string | undefined;
369
+ if (flowId) {
370
+ if (method !== "POST" || q.redirect !== undefined) {
371
+ setResponseStatus(event, 400);
372
+ return { error: "Invalid desktop exchange challenge." };
373
+ }
374
+ const verifier = getHeader(event, "x-agent-native-desktop-verifier");
375
+ if (!verifier || q.verifier !== undefined) {
376
+ setResponseStatus(event, 400);
377
+ return { error: "Invalid desktop exchange challenge." };
378
+ }
379
+ try {
380
+ desktopBrowserBindingHash = prepareDesktopOAuthBrowserBinding(event);
381
+ desktopVerifierHash = await registerDesktopExchange(
382
+ flowId,
383
+ verifier,
384
+ desktopBrowserBindingHash,
385
+ );
386
+ } catch {
387
+ setResponseStatus(event, 400);
388
+ return { error: "Invalid desktop exchange challenge." };
389
+ }
390
+ }
298
391
  const state = encodeOAuthState({
299
392
  redirectUri,
300
393
  owner: session.email,
@@ -302,6 +395,8 @@ export const getGoogleAddAccountUrl = defineEventHandler(
302
395
  addAccount: true,
303
396
  app: OAUTH_STATE_APP_ID,
304
397
  flowId,
398
+ desktopVerifierHash,
399
+ desktopBrowserBindingHash,
305
400
  });
306
401
  const url = await getAuthUrl(
307
402
  undefined,
@@ -333,22 +428,28 @@ export const handleGoogleAddAccountCallback = defineEventHandler(
333
428
  );
334
429
  desktop = state.desktop ?? false;
335
430
  flowId = state.flowId;
431
+ if (
432
+ flowId &&
433
+ (!state.desktopVerifierHash ||
434
+ !state.desktopBrowserBindingHash ||
435
+ !matchesDesktopOAuthBrowserBinding(
436
+ event,
437
+ state.desktopBrowserBindingHash,
438
+ ))
439
+ ) {
440
+ throw new Error("Desktop OAuth browser binding is invalid.");
441
+ }
336
442
 
337
443
  // Handle Google authorization errors (e.g. user denied access, invalid client)
338
444
  const googleError = query.error as string | undefined;
339
445
  if (googleError) {
340
446
  const errorDesc =
341
447
  (query.error_description as string | undefined) || googleError;
342
- const isPermission =
343
- googleError === "access_denied" ||
344
- errorDesc.includes("Insufficient Permission");
345
- const userMessage = isPermission
346
- ? "Access was denied. Make sure to check all the permission boxes on the consent screen. If the app is in testing mode, add this email as a test user in Google Cloud Console."
347
- : `Connection failed: ${errorDesc}`;
348
- return googleOAuthErrorResponse(event, new Error(userMessage), {
349
- desktop,
350
- flowId,
351
- });
448
+ return googleOAuthErrorResponse(
449
+ event,
450
+ Object.assign(new Error(errorDesc), { oauthErrorCode: googleError }),
451
+ { desktop, flowId },
452
+ );
352
453
  }
353
454
 
354
455
  const { redirectUri, owner: stateOwner } = state;
@@ -6,8 +6,11 @@ import { isSelfAddressedThread } from "@shared/self-notes.js";
6
6
  import type { EmailMessage } from "@shared/types.js";
7
7
 
8
8
  export const VIEW_QUERIES: Record<string, string> = {
9
- inbox: "in:inbox -in:sent",
10
- unread: "is:unread in:inbox -in:sent",
9
+ // Keep sent replies in a matching Gmail thread. The local thread filter
10
+ // below excludes sent-only threads while preserving received inbox threads
11
+ // whose latest message was sent by the user.
12
+ inbox: "in:inbox",
13
+ unread: "is:unread in:inbox",
11
14
  starred: "is:starred",
12
15
  sent: "in:sent",
13
16
  drafts: "in:drafts",
@@ -0,0 +1 @@
1
+ export { default } from "../../../../_agent-native/google/add-account/auth-url.post.js";
@@ -0,0 +1 @@
1
+ export { default } from "../../../_agent-native/google/auth-url.post.js";
@@ -0,0 +1 @@
1
+ export { getGoogleAddAccountUrl as default } from "../../../../handlers/google-auth.js";