@agent-native/core 0.164.3 → 0.164.11

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 (234) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +36 -47
  3. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +3 -0
  4. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +18 -1
  5. package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +256 -38
  6. package/corpus/templates/calendar/app/hooks/use-google-auth.ts +43 -3
  7. package/corpus/templates/calendar/app/i18n-data.ts +120 -0
  8. package/corpus/templates/calendar/app/lib/event-form-utils.ts +113 -0
  9. package/corpus/templates/calendar/server/handlers/google-auth.ts +119 -3
  10. package/corpus/templates/calendar/server/routes/[base]/_agent-native/google/add-account/auth-url.post.ts +1 -0
  11. package/corpus/templates/calendar/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
  12. package/corpus/templates/calendar/server/routes/_agent-native/google/add-account/auth-url.post.ts +1 -0
  13. package/corpus/templates/calendar/server/routes/_agent-native/google/auth-url.post.ts +1 -0
  14. package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +1 -1
  15. package/corpus/templates/clips/app/components/player/video-player.tsx +12 -0
  16. package/corpus/templates/clips/app/routes/_app.dictate.tsx +18 -4
  17. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +114 -20
  18. package/corpus/templates/clips/app/routes/share.$shareId.tsx +86 -78
  19. package/corpus/templates/clips/changelog/2026-08-19-clips-nightly-builds-are-available-for-trying-the-latest-upd.md +6 -0
  20. package/corpus/templates/clips/changelog/2026-08-19-social-preview-video-frames.md +6 -0
  21. package/corpus/templates/clips/desktop/README.md +19 -4
  22. package/corpus/templates/clips/desktop/src/app.tsx +78 -16
  23. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +3 -2
  24. package/corpus/templates/clips/desktop/src-tauri/tauri.conf.json +1 -0
  25. package/corpus/templates/clips/server/lib/slack-unfurls.ts +7 -13
  26. package/corpus/templates/clips/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
  27. package/corpus/templates/clips/server/routes/_agent-native/google/auth-url.get.ts +41 -3
  28. package/corpus/templates/clips/server/routes/_agent-native/google/auth-url.post.ts +1 -0
  29. package/corpus/templates/clips/server/routes/_agent-native/google/callback.get.ts +22 -1
  30. package/corpus/templates/clips/server/routes/api/clips-updater.json.get.ts +41 -22
  31. package/corpus/templates/clips/shared/share-meta.ts +67 -3
  32. package/corpus/templates/content/actions/list-content-databases.ts +95 -1
  33. package/corpus/templates/content/changelog/2026-08-11-content-mutations-delegated-from-verified-slack-dms-now-use-.md +6 -0
  34. package/corpus/templates/content/shared/api.ts +11 -0
  35. package/corpus/templates/factory/AGENTS.md +1 -1
  36. package/corpus/templates/factory/actions/list-factory-automations.ts +4 -0
  37. package/corpus/templates/factory/actions/start-builder-for-item.ts +19 -17
  38. package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +100 -36
  39. package/corpus/templates/factory/app/i18n/en-US.ts +4 -0
  40. package/corpus/templates/factory/app/routes/factory.tsx +209 -46
  41. package/corpus/templates/factory/server/connectors/slack.ts +44 -2
  42. package/corpus/templates/factory/server/plugins/agent-chat.ts +9 -7
  43. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +13 -0
  44. package/corpus/templates/factory/server/triage/slack-client.ts +5 -0
  45. package/corpus/templates/factory/server/triage/slack-poller.ts +72 -4
  46. package/corpus/templates/mail/actions/view-screen.ts +1 -1
  47. package/corpus/templates/mail/app/components/GoogleConnectBanner.tsx +100 -18
  48. package/corpus/templates/mail/server/handlers/google-auth.ts +125 -24
  49. package/corpus/templates/mail/server/lib/gmail-query.ts +5 -2
  50. package/corpus/templates/mail/server/routes/[base]/_agent-native/google/add-account/auth-url.post.ts +1 -0
  51. package/corpus/templates/mail/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
  52. package/corpus/templates/mail/server/routes/_agent-native/google/add-account/auth-url.post.ts +1 -0
  53. package/corpus/templates/mail/server/routes/_agent-native/google/auth-url.post.ts +1 -0
  54. package/corpus/templates/plan/app/pages/PlansPage.tsx +0 -12
  55. package/corpus/templates/plan/changelog/2026-08-19-plan-prompts-now-guide-you-to-connect-ai-before-sending-and-.md +6 -0
  56. package/corpus/templates/slides/actions/approve-deck-access-request.ts +234 -0
  57. package/corpus/templates/slides/actions/get-deck-access-status.ts +35 -2
  58. package/corpus/templates/slides/actions/request-deck-access.ts +263 -64
  59. package/corpus/templates/slides/app/components/editor/MissingDeckAccessPane.tsx +196 -0
  60. package/corpus/templates/slides/app/components/editor/RequestDeckAccessDialog.tsx +101 -0
  61. package/corpus/templates/slides/app/hooks/use-deck-access.ts +7 -4
  62. package/corpus/templates/slides/app/i18n/en-US.ts +29 -6
  63. package/corpus/templates/slides/app/pages/DeckEditor.tsx +147 -169
  64. package/corpus/templates/slides/app/routes/access-request.approve.tsx +250 -0
  65. package/corpus/templates/slides/server/db/schema.ts +8 -0
  66. package/corpus/templates/slides/server/lib/access-request-email.ts +36 -0
  67. package/corpus/templates/slides/server/lib/emails.ts +26 -0
  68. package/corpus/templates/slides/server/plugins/agent-chat.ts +7 -0
  69. package/corpus/templates/slides/server/plugins/db.ts +50 -0
  70. package/corpus/templates/slides/shared/deck-access.ts +31 -0
  71. package/dist/a2a/artifact-response.d.ts +50 -0
  72. package/dist/a2a/artifact-response.js +266 -39
  73. package/dist/a2a/index.d.ts +2 -0
  74. package/dist/a2a/index.js +1 -0
  75. package/dist/a2a/task-store.d.ts +1 -0
  76. package/dist/a2a/task-store.js +1 -1
  77. package/dist/agent/observational-memory/store.d.ts +1 -0
  78. package/dist/agent/observational-memory/store.js +1 -1
  79. package/dist/agent/run-store.d.ts +1 -0
  80. package/dist/agent/run-store.js +1 -1
  81. package/dist/agent/tool-approval-store.d.ts +1 -0
  82. package/dist/agent/tool-approval-store.js +1 -1
  83. package/dist/application-state/store.d.ts +1 -0
  84. package/dist/application-state/store.js +1 -1
  85. package/dist/browser-sessions/store.d.ts +1 -0
  86. package/dist/browser-sessions/store.js +1 -1
  87. package/dist/checkpoints/store.d.ts +1 -0
  88. package/dist/checkpoints/store.js +1 -1
  89. package/dist/cli/code-agent-schedules.d.ts +11 -0
  90. package/dist/cli/code-agent-schedules.js +44 -5
  91. package/dist/client/AgentPanel.d.ts +5 -1
  92. package/dist/client/AgentPanel.js +2 -2
  93. package/dist/client/AssistantChat.d.ts +5 -1
  94. package/dist/client/AssistantChat.js +40 -6
  95. package/dist/client/EnvironmentBadge.d.ts +7 -13
  96. package/dist/client/EnvironmentBadge.js +43 -92
  97. package/dist/client/agent-chat/index.d.ts +1 -0
  98. package/dist/client/agent-chat/index.js +1 -0
  99. package/dist/client/agent-chat-adapter.js +76 -50
  100. package/dist/client/app-providers.js +4 -4
  101. package/dist/client/chat/message-components.d.ts +4 -0
  102. package/dist/client/chat/message-components.js +28 -1
  103. package/dist/client/chat/run-recovery.js +26 -0
  104. package/dist/client/chat-first/apps-rail.d.ts +1 -0
  105. package/dist/client/chat-first/apps-rail.js +28 -2
  106. package/dist/client/sse-event-processor.js +13 -8
  107. package/dist/client/use-active-agent-chat-run.d.ts +2 -0
  108. package/dist/client/use-active-agent-chat-run.js +22 -0
  109. package/dist/coding-tools/sandbox/executions-store.d.ts +1 -0
  110. package/dist/coding-tools/sandbox/executions-store.js +1 -1
  111. package/dist/collab/awareness-store.d.ts +1 -0
  112. package/dist/collab/awareness-store.js +1 -1
  113. package/dist/collab/routes.d.ts +1 -1
  114. package/dist/collab/storage.d.ts +1 -0
  115. package/dist/collab/storage.js +1 -1
  116. package/dist/db/client.js +3 -3
  117. package/dist/db/ddl-guard.js +8 -0
  118. package/dist/db/migration-runtime.d.ts +23 -0
  119. package/dist/db/migration-runtime.js +41 -0
  120. package/dist/db/migrations.d.ts +1 -2
  121. package/dist/db/migrations.js +2 -24
  122. package/dist/email-catalog/log.d.ts +1 -0
  123. package/dist/email-catalog/log.js +1 -1
  124. package/dist/guards/index.d.ts +1 -0
  125. package/dist/guards/index.js +1 -0
  126. package/dist/guards/release-schema-complete.d.ts +22 -0
  127. package/dist/guards/release-schema-complete.js +144 -0
  128. package/dist/integrations/a2a-continuations-store.d.ts +1 -0
  129. package/dist/integrations/a2a-continuations-store.js +1 -1
  130. package/dist/integrations/adapters/slack.js +58 -7
  131. package/dist/integrations/awaiting-input-store.d.ts +1 -0
  132. package/dist/integrations/awaiting-input-store.js +1 -1
  133. package/dist/integrations/config-store.d.ts +1 -0
  134. package/dist/integrations/config-store.js +1 -1
  135. package/dist/integrations/controls-store.d.ts +1 -0
  136. package/dist/integrations/controls-store.js +1 -1
  137. package/dist/integrations/identity-links-store.d.ts +1 -0
  138. package/dist/integrations/identity-links-store.js +1 -1
  139. package/dist/integrations/installations-store.d.ts +1 -0
  140. package/dist/integrations/installations-store.js +1 -1
  141. package/dist/integrations/remote-commands-store.d.ts +1 -0
  142. package/dist/integrations/remote-commands-store.js +1 -1
  143. package/dist/integrations/remote-devices-store.d.ts +1 -0
  144. package/dist/integrations/remote-devices-store.js +1 -1
  145. package/dist/integrations/remote-push-store.d.ts +1 -0
  146. package/dist/integrations/remote-push-store.js +1 -1
  147. package/dist/integrations/remote-run-events-store.d.ts +1 -0
  148. package/dist/integrations/remote-run-events-store.js +1 -1
  149. package/dist/integrations/scope-store.d.ts +1 -0
  150. package/dist/integrations/scope-store.js +1 -1
  151. package/dist/integrations/thread-mapping-store.d.ts +1 -0
  152. package/dist/integrations/thread-mapping-store.js +1 -1
  153. package/dist/integrations/usage-budget-store.d.ts +1 -0
  154. package/dist/integrations/usage-budget-store.js +1 -1
  155. package/dist/jobs/background-automation-runner.js +111 -9
  156. package/dist/jobs/run-history.d.ts +1 -0
  157. package/dist/jobs/run-history.js +1 -1
  158. package/dist/jobs/scheduler-health.d.ts +1 -0
  159. package/dist/jobs/scheduler-health.js +1 -1
  160. package/dist/mcp/approval-store.d.ts +1 -0
  161. package/dist/mcp/approval-store.js +1 -1
  162. package/dist/mcp/connect-store.d.ts +1 -0
  163. package/dist/mcp/connect-store.js +1 -1
  164. package/dist/mcp/oauth-store.d.ts +1 -0
  165. package/dist/mcp/oauth-store.js +1 -1
  166. package/dist/notifications/routes.d.ts +3 -3
  167. package/dist/notifications/store.d.ts +1 -0
  168. package/dist/notifications/store.js +1 -1
  169. package/dist/oauth-tokens/store.d.ts +1 -0
  170. package/dist/oauth-tokens/store.js +1 -1
  171. package/dist/observability/routes.d.ts +3 -3
  172. package/dist/progress/routes.d.ts +1 -1
  173. package/dist/progress/store.d.ts +1 -0
  174. package/dist/progress/store.js +1 -1
  175. package/dist/provider-api/actions/custom-provider-registration.d.ts +5 -5
  176. package/dist/provider-api/actions/provider-api.d.ts +4 -4
  177. package/dist/provider-api/corpus-jobs-store.d.ts +1 -0
  178. package/dist/provider-api/corpus-jobs-store.js +1 -1
  179. package/dist/provider-api/custom-registry.d.ts +1 -0
  180. package/dist/provider-api/custom-registry.js +1 -1
  181. package/dist/provider-api/quota-governor.d.ts +1 -0
  182. package/dist/provider-api/quota-governor.js +1 -1
  183. package/dist/provider-api/staged-datasets-store.d.ts +1 -0
  184. package/dist/provider-api/staged-datasets-store.js +1 -1
  185. package/dist/resources/handlers.d.ts +3 -3
  186. package/dist/resources/store.d.ts +1 -0
  187. package/dist/resources/store.js +1 -1
  188. package/dist/scripts/dev/index.js +15 -1
  189. package/dist/search/index.js +3 -0
  190. package/dist/secrets/storage.d.ts +1 -0
  191. package/dist/secrets/storage.js +1 -1
  192. package/dist/server/agent-chat/action-filters-a2a.d.ts +2 -1
  193. package/dist/server/agent-chat/action-filters-a2a.js +10 -2
  194. package/dist/server/agent-chat-plugin.js +71 -20
  195. package/dist/server/agent-teams-run-queue.d.ts +1 -0
  196. package/dist/server/agent-teams-run-queue.js +1 -1
  197. package/dist/server/auth-marketing.js +3 -1
  198. package/dist/server/auth.d.ts +18 -1
  199. package/dist/server/auth.js +370 -154
  200. package/dist/server/embed-session.d.ts +1 -0
  201. package/dist/server/embed-session.js +1 -1
  202. package/dist/server/google-auth-plugin.js +3 -1
  203. package/dist/server/google-oauth.d.ts +10 -0
  204. package/dist/server/google-oauth.js +25 -1
  205. package/dist/server/identity-sso-store.d.ts +1 -0
  206. package/dist/server/identity-sso-store.js +1 -1
  207. package/dist/server/index.d.ts +1 -1
  208. package/dist/server/index.js +1 -1
  209. package/dist/server/onboarding-html.js +200 -31
  210. package/dist/server/poll.d.ts +2 -0
  211. package/dist/server/poll.js +19 -1
  212. package/dist/server/realtime-token.d.ts +1 -1
  213. package/dist/server/release-migrations.js +5 -0
  214. package/dist/server/release-schema.d.ts +46 -0
  215. package/dist/server/release-schema.js +307 -0
  216. package/dist/settings/store.d.ts +1 -0
  217. package/dist/settings/store.js +1 -1
  218. package/dist/shared/environment-lanes.d.ts +28 -0
  219. package/dist/shared/environment-lanes.js +61 -0
  220. package/dist/sharing/actions/share-resource.js +22 -2
  221. package/dist/styles/agent-native.css +17 -15
  222. package/dist/triggers/routes.d.ts +11 -0
  223. package/dist/triggers/routes.js +10 -0
  224. package/dist/usage/alerts-store.d.ts +1 -0
  225. package/dist/usage/alerts-store.js +1 -1
  226. package/dist/usage/store.d.ts +1 -0
  227. package/dist/usage/store.js +1 -1
  228. package/docs/content/drop-in-agent.mdx +4 -4
  229. package/docs/content/faq.mdx +15 -15
  230. package/docs/content/getting-started.mdx +173 -41
  231. package/docs/content/key-concepts.mdx +10 -10
  232. package/docs/content/using-your-agent.mdx +2 -2
  233. package/docs/content/what-is-agent-native.mdx +79 -228
  234. package/package.json +2 -2
@@ -6,6 +6,7 @@ import { eq } from "drizzle-orm";
6
6
  import {
7
7
  clipsShareDescription,
8
8
  displayRecordingTitle,
9
+ resolveClipsSocialImageUrl,
9
10
  } from "../../shared/share-meta.js";
10
11
  import { getDb, schema } from "../db/index.js";
11
12
 
@@ -191,15 +192,6 @@ function appPath(path: string, basePath: string): string {
191
192
  return base ? `/${base}${path}` : path;
192
193
  }
193
194
 
194
- function absoluteUrl(value: string | null | undefined, origin: string): string {
195
- if (!value) return AGENT_NATIVE_DEFAULT_SOCIAL_IMAGE;
196
- try {
197
- return new URL(value, origin).toString();
198
- } catch {
199
- return AGENT_NATIVE_DEFAULT_SOCIAL_IMAGE;
200
- }
201
- }
202
-
203
195
  function isExpired(value: string | null): boolean {
204
196
  if (!value) return false;
205
197
  const expires = new Date(value).getTime();
@@ -255,10 +247,12 @@ export function buildSlackVideoBlock(options: {
255
247
  const description = duration
256
248
  ? `${duration} · ${shareDescription}`
257
249
  : shareDescription;
258
- const thumbnailUrl = absoluteUrl(
259
- recording.thumbnailUrl || recording.animatedThumbnailUrl,
260
- origin,
261
- );
250
+ const thumbnailUrl =
251
+ resolveClipsSocialImageUrl({
252
+ recording,
253
+ origin,
254
+ basePath,
255
+ }) ?? AGENT_NATIVE_DEFAULT_SOCIAL_IMAGE;
262
256
 
263
257
  return {
264
258
  type: "video",
@@ -0,0 +1 @@
1
+ export { default } from "../../../_agent-native/google/auth-url.get.js";
@@ -4,10 +4,14 @@ import {
4
4
  isElectron,
5
5
  resolveGoogleSignInCredentials,
6
6
  resolveOAuthRedirectUri,
7
+ registerDesktopExchange,
8
+ prepareDesktopOAuthBrowserBinding,
7
9
  safeReturnPath,
8
10
  } from "@agent-native/core/server";
9
11
  import {
10
12
  defineEventHandler,
13
+ getHeader,
14
+ getMethod,
11
15
  getQuery,
12
16
  setResponseStatus,
13
17
  type H3Event,
@@ -54,11 +58,39 @@ export default defineEventHandler(async (event: H3Event) => {
54
58
  isElectron(event) || q.desktop === "1" || q.desktop === "true";
55
59
  const flowId =
56
60
  desktop && typeof q.flow_id === "string" ? q.flow_id : undefined;
61
+ if (getMethod(event) === "POST" && (!desktop || !flowId)) {
62
+ setResponseStatus(event, 400);
63
+ return { error: "Invalid desktop exchange challenge." };
64
+ }
65
+ const calendarConnect =
66
+ q.calendar === "1" || q.calendar === "true" || q.product === "calendar";
67
+ let desktopVerifierHash: string | undefined;
68
+ let desktopBrowserBindingHash: string | undefined;
69
+ if (flowId && !calendarConnect) {
70
+ if (getMethod(event) !== "POST" || q.redirect !== undefined) {
71
+ setResponseStatus(event, 400);
72
+ return { error: "Invalid desktop exchange challenge." };
73
+ }
74
+ const verifier = getHeader(event, "x-agent-native-desktop-verifier");
75
+ if (!verifier || q.verifier !== undefined) {
76
+ setResponseStatus(event, 400);
77
+ return { error: "Invalid desktop exchange challenge." };
78
+ }
79
+ try {
80
+ desktopBrowserBindingHash = prepareDesktopOAuthBrowserBinding(event);
81
+ desktopVerifierHash = await registerDesktopExchange(
82
+ flowId,
83
+ verifier,
84
+ desktopBrowserBindingHash,
85
+ );
86
+ } catch {
87
+ setResponseStatus(event, 400);
88
+ return { error: "Invalid desktop exchange challenge." };
89
+ }
90
+ }
57
91
  const requestedReturn =
58
92
  typeof q.return === "string" ? safeReturnPath(q.return) : "/";
59
93
  const returnUrl = requestedReturn !== "/" ? requestedReturn : undefined;
60
- const calendarConnect =
61
- q.calendar === "1" || q.calendar === "true" || q.product === "calendar";
62
94
  const credentials = calendarConnect
63
95
  ? ((await resolveGoogleOAuthCredentialCandidates())[0] ?? null)
64
96
  : resolveGoogleSignInCredentials();
@@ -84,10 +116,16 @@ export default defineEventHandler(async (event: H3Event) => {
84
116
  redirectUri,
85
117
  owner,
86
118
  desktop,
119
+ desktopWebview: desktop && q.webview === "1" && !calendarConnect,
87
120
  addAccount: calendarConnect,
88
121
  app: CLIPS_GOOGLE_OAUTH_APP_ID,
89
- returnUrl,
122
+ returnUrl:
123
+ desktop && q.webview === "1" && !calendarConnect
124
+ ? "/?desktop_auth=complete"
125
+ : returnUrl,
90
126
  flowId: calendarConnect ? undefined : flowId,
127
+ desktopVerifierHash,
128
+ desktopBrowserBindingHash,
91
129
  });
92
130
 
93
131
  const params = new URLSearchParams({
@@ -0,0 +1 @@
1
+ export { default } from "./auth-url.get.js";
@@ -6,6 +6,7 @@ import {
6
6
  oauthErrorPage,
7
7
  resolveGoogleSignInCredentials,
8
8
  resolveOAuthOwner,
9
+ matchesDesktopOAuthBrowserBinding,
9
10
  setDesktopExchange,
10
11
  type OAuthStatePayload,
11
12
  } from "@agent-native/core/server";
@@ -31,6 +32,17 @@ async function handleGoogleSignInCallback(
31
32
  ) {
32
33
  const desktop = state.desktop;
33
34
  const flowId = state.flowId;
35
+ if (
36
+ flowId &&
37
+ (!state.desktopVerifierHash ||
38
+ !state.desktopBrowserBindingHash ||
39
+ !matchesDesktopOAuthBrowserBinding(
40
+ event,
41
+ state.desktopBrowserBindingHash,
42
+ ))
43
+ ) {
44
+ return oauthErrorPage("Desktop OAuth browser binding is invalid.");
45
+ }
34
46
 
35
47
  try {
36
48
  const query = getQuery(event);
@@ -107,12 +119,21 @@ async function handleGoogleSignInCallback(
107
119
  });
108
120
 
109
121
  if (flowId && sessionToken) {
110
- setDesktopExchange(flowId, sessionToken, email);
122
+ if (!state.desktopVerifierHash) {
123
+ throw new Error("Missing desktop exchange challenge.");
124
+ }
125
+ await setDesktopExchange(
126
+ flowId,
127
+ sessionToken,
128
+ email,
129
+ state.desktopVerifierHash,
130
+ );
111
131
  }
112
132
 
113
133
  return oauthCallbackResponse(event, email, {
114
134
  sessionToken,
115
135
  desktop,
136
+ desktopWebview: state.desktopWebview,
116
137
  returnUrl: state.returnUrl,
117
138
  flowId,
118
139
  appName: "Clips",
@@ -1,17 +1,23 @@
1
- import { defineEventHandler, setResponseHeaders } from "h3";
1
+ import { defineEventHandler, getQuery, setResponseHeaders } from "h3";
2
2
 
3
3
  /**
4
4
  * Tauri updater manifest endpoint for the Clips desktop app.
5
5
  *
6
6
  * The installed app only needs this endpoint to be valid JSON when no signed
7
- * updater bundle is available. If the `clips-latest` GitHub pointer release
8
- * exists, we proxy its signed manifest. If it does not, return a deliberately
9
- * old no-update manifest so the desktop UI stays quiet instead of surfacing a
7
+ * updater bundle is available. If the channel's GitHub pointer release exists,
8
+ * we proxy its signed manifest. If it does not, return a deliberately old
9
+ * no-update manifest so the desktop UI stays quiet instead of surfacing a
10
10
  * release-channel setup error to end users.
11
11
  */
12
12
 
13
- const GITHUB_MANIFEST_URL =
14
- "https://github.com/BuilderIO/agent-native/releases/download/clips-latest/clips-latest.json";
13
+ type ClipsUpdateChannel = "production" | "nightly";
14
+
15
+ const GITHUB_MANIFEST_URL: Record<ClipsUpdateChannel, string> = {
16
+ production:
17
+ "https://github.com/BuilderIO/agent-native/releases/download/clips-latest/clips-latest.json",
18
+ nightly:
19
+ "https://github.com/BuilderIO/agent-native/releases/download/clips-nightly-latest/clips-nightly-latest.json",
20
+ };
15
21
  const CACHE_TTL_MS = 5 * 60_000;
16
22
 
17
23
  // Tauri throws a red-banner error if the requesting client's target triple is
@@ -48,8 +54,8 @@ export const INERT_MANIFEST = {
48
54
  },
49
55
  };
50
56
 
51
- let cache: { data: unknown; ts: number } | null = null;
52
- let inFlight: Promise<unknown> | null = null;
57
+ const cache = new Map<ClipsUpdateChannel, { data: unknown; ts: number }>();
58
+ const inFlight = new Map<ClipsUpdateChannel, Promise<unknown>>();
53
59
 
54
60
  function isManifestLike(value: unknown): value is Record<string, unknown> {
55
61
  if (!value || typeof value !== "object") return false;
@@ -72,8 +78,10 @@ export function hasAllRequiredPlatforms(value: unknown): boolean {
72
78
  return REQUIRED_PLATFORM_KEYS.every((k) => k in platforms);
73
79
  }
74
80
 
75
- async function fetchSignedManifest(): Promise<unknown> {
76
- const res = await fetch(GITHUB_MANIFEST_URL, {
81
+ async function fetchSignedManifest(
82
+ channel: ClipsUpdateChannel,
83
+ ): Promise<unknown> {
84
+ const res = await fetch(GITHUB_MANIFEST_URL[channel], {
77
85
  headers: {
78
86
  accept: "application/json",
79
87
  "user-agent": "clips-updater-manifest",
@@ -94,38 +102,49 @@ async function fetchSignedManifest(): Promise<unknown> {
94
102
  return json;
95
103
  }
96
104
 
97
- async function getManifest(): Promise<unknown> {
105
+ function normalizeChannel(value: unknown): ClipsUpdateChannel {
106
+ return value === "nightly" ? "nightly" : "production";
107
+ }
108
+
109
+ async function getManifest(
110
+ channel: ClipsUpdateChannel = "production",
111
+ ): Promise<unknown> {
98
112
  const now = Date.now();
99
- if (cache && now - cache.ts < CACHE_TTL_MS) return cache.data;
100
- if (inFlight) return inFlight;
101
- inFlight = (async () => {
113
+ const cached = cache.get(channel);
114
+ if (cached && now - cached.ts < CACHE_TTL_MS) return cached.data;
115
+ const pending = inFlight.get(channel);
116
+ if (pending) return pending;
117
+ const request = (async () => {
102
118
  try {
103
- const data = await fetchSignedManifest();
104
- cache = { data, ts: Date.now() };
119
+ const data = await fetchSignedManifest(channel);
120
+ cache.set(channel, { data, ts: Date.now() });
105
121
  return data;
106
122
  } catch {
107
123
  // A validation or network failure may serve the last good manifest (or
108
124
  // inert fallback), but it must not make that fallback fresh again.
109
125
  // Otherwise an incomplete release manifest can indefinitely extend the
110
126
  // stale cache window on every failed refresh.
111
- return cache?.data ?? INERT_MANIFEST;
127
+ return cache.get(channel)?.data ?? INERT_MANIFEST;
112
128
  } finally {
113
- inFlight = null;
129
+ inFlight.delete(channel);
114
130
  }
115
131
  })();
116
- return inFlight;
132
+ inFlight.set(channel, request);
133
+ return request;
117
134
  }
118
135
 
119
136
  export const __clipsUpdaterTest = {
120
137
  getManifest,
121
138
  reset() {
122
- cache = null;
123
- inFlight = null;
139
+ cache.clear();
140
+ inFlight.clear();
124
141
  },
125
142
  };
126
143
 
127
144
  export default defineEventHandler(async (event) => {
128
- const manifest = await getManifest();
145
+ const query = getQuery(event);
146
+ const channel = normalizeChannel(query.channel);
147
+ const manifest = await getManifest(channel);
129
148
  setResponseHeaders(event, {
130
149
  "content-type": "application/json; charset=utf-8",
131
150
  "cache-control": "public, max-age=60",
@@ -8,15 +8,25 @@ import {
8
8
  type SocialMetaDescriptor,
9
9
  } from "@agent-native/core/shared";
10
10
 
11
+ import { buildAgentApiUrls } from "./agent-context";
12
+
11
13
  export const CLIPS_DEFAULT_TITLE = "Untitled recording";
12
14
 
13
15
  export type ClipsShareMetaRecording = {
16
+ id?: string | null;
14
17
  title?: string | null;
15
18
  description?: string | null;
16
19
  thumbnailUrl?: string | null;
17
20
  animatedThumbnailUrl?: string | null;
21
+ visibility?: string | null;
22
+ status?: string | null;
23
+ hasPassword?: boolean;
24
+ archivedAt?: string | null;
25
+ trashedAt?: string | null;
18
26
  };
19
27
 
28
+ const SOCIAL_FRAME_AT_MS = 350;
29
+
20
30
  export type PreferredThumbnailVariant = "still" | "animated";
21
31
 
22
32
  export function preferredThumbnailVariant(
@@ -81,16 +91,70 @@ function absoluteUrl(value: string, origin: string | null): string {
81
91
  }
82
92
  }
83
93
 
94
+ function appPath(path: string, basePath: string): string {
95
+ const normalizedBasePath = basePath.trim().replace(/\/+$/, "");
96
+ return normalizedBasePath ? `${normalizedBasePath}${path}` : path;
97
+ }
98
+
99
+ function canUseGeneratedSocialFrame(
100
+ recording: ClipsShareMetaRecording | null,
101
+ ): recording is ClipsShareMetaRecording & {
102
+ id: string;
103
+ visibility: "public";
104
+ status: "ready";
105
+ } {
106
+ return Boolean(
107
+ recording?.id &&
108
+ recording.visibility === "public" &&
109
+ recording.status === "ready" &&
110
+ !recording.hasPassword &&
111
+ !recording.archivedAt &&
112
+ !recording.trashedAt,
113
+ );
114
+ }
115
+
116
+ export function resolveClipsSocialImageUrl(options: {
117
+ recording: ClipsShareMetaRecording | null;
118
+ origin?: string | null;
119
+ basePath?: string;
120
+ }): string | undefined {
121
+ const { recording, origin = null, basePath = "" } = options;
122
+ if (recording?.hasPassword) return undefined;
123
+
124
+ const storedImage = preferredSocialImage(recording);
125
+
126
+ if (storedImage) {
127
+ if (recording?.id && recording.visibility === "public") {
128
+ return absoluteUrl(
129
+ appPath(`/api/thumbnail/${encodeURIComponent(recording.id)}`, basePath),
130
+ origin,
131
+ );
132
+ }
133
+ return absoluteUrl(storedImage, origin);
134
+ }
135
+
136
+ if (!origin || !canUseGeneratedSocialFrame(recording)) return undefined;
137
+
138
+ return buildAgentApiUrls(recording.id, {
139
+ origin,
140
+ basePath,
141
+ }).frameUrl(SOCIAL_FRAME_AT_MS);
142
+ }
143
+
84
144
  export function buildClipsShareMeta(options: {
85
145
  recording: ClipsShareMetaRecording | null;
86
146
  origin?: string | null;
147
+ basePath?: string;
87
148
  shareUrl?: string | null;
88
149
  }): SocialMetaDescriptor[] {
89
- const { recording, origin = null, shareUrl = null } = options;
150
+ const { recording, origin = null, basePath = "", shareUrl = null } = options;
90
151
  const title = clipsSharePageTitle(recording?.title);
91
152
  const description = clipsShareDescription(recording);
92
- const image = preferredSocialImage(recording);
93
- const absoluteImage = image ? absoluteUrl(image, origin) : undefined;
153
+ const absoluteImage = resolveClipsSocialImageUrl({
154
+ recording,
155
+ origin,
156
+ basePath,
157
+ });
94
158
  const alt = hasGeneratedTitle(recording?.title)
95
159
  ? recording!.title!.trim()
96
160
  : AGENT_NATIVE_SOCIAL_IMAGE_ALT;
@@ -1,10 +1,12 @@
1
1
  import { defineAction } from "@agent-native/core";
2
+ import { getRequestUserEmail } from "@agent-native/core/server/request-context";
2
3
  import { accessFilter } from "@agent-native/core/sharing";
3
4
  import {
4
5
  and,
5
6
  asc,
6
7
  eq,
7
8
  inArray,
9
+ isNotNull,
8
10
  isNull,
9
11
  notInArray,
10
12
  or,
@@ -15,6 +17,11 @@ import { z } from "zod";
15
17
  import { getDb, schema } from "../server/db/index.js";
16
18
  import { documentDiscoveryFilter } from "../server/lib/documents.js";
17
19
  import type { ListContentDatabasesResponse } from "../shared/api.js";
20
+ import {
21
+ listContentOrganizationMemberships,
22
+ normalizeContentSpaceEmail,
23
+ resolveContentSpaceAccess,
24
+ } from "./_content-space-access.js";
18
25
  import { documentDiscoveryPagination } from "./_document-discovery-query.js";
19
26
 
20
27
  const DEFAULT_CONTENT_DATABASE_DISCOVERY_LIMIT = 50;
@@ -27,7 +34,7 @@ export class ContentDatabaseResolutionError extends Error {}
27
34
 
28
35
  export default defineAction({
29
36
  description:
30
- "Discover one bounded page of ordinary Content databases the user can access from their live title and user-authored description. Returns stable database, document, and space IDs with explicit pagination; follow nextOffset until hasMore is false. Use exact filters before reading a selected database's schema.",
37
+ "Discover one bounded page of ordinary Content databases the user can access from their live title and user-authored description. Returns stable database, document, and space IDs with explicit pagination; follow nextOffset until hasMore is false. Use exact filters before reading a selected database's schema. Set includeSystemCollections to classify Files, Favorites, Workspaces, and other system chrome separately from ordinary databases.",
31
38
  schema: z.object({
32
39
  spaceId: z
33
40
  .string()
@@ -39,6 +46,12 @@ export default defineAction({
39
46
  .min(1)
40
47
  .optional()
41
48
  .describe("Exact Content database ID to resolve."),
49
+ includeSystemCollections: z
50
+ .boolean()
51
+ .optional()
52
+ .describe(
53
+ "Return system-role databases in systemCollections instead of treating them as ordinary databases.",
54
+ ),
42
55
  documentId: z
43
56
  .string()
44
57
  .min(1)
@@ -80,7 +93,21 @@ export default defineAction({
80
93
  readOnly: true,
81
94
  publicAgent: { expose: true, readOnly: true, requiresAuth: true },
82
95
  run: async (args): Promise<ListContentDatabasesResponse> => {
96
+ if (args.includeSystemCollections && !args.spaceId) {
97
+ throw new ContentDatabaseResolutionError(
98
+ "An exact spaceId is required to inventory system collections.",
99
+ );
100
+ }
83
101
  const db = getDb();
102
+ const requestUserEmail = getRequestUserEmail();
103
+ if (!requestUserEmail) throw new Error("no authenticated user");
104
+ const normalizedUserEmail = normalizeContentSpaceEmail(requestUserEmail);
105
+ const authorizedOrganizationIds = (
106
+ await listContentOrganizationMemberships(normalizedUserEmail)
107
+ ).map((membership) => membership.orgId);
108
+ if (args.spaceId) {
109
+ await resolveContentSpaceAccess(args.spaceId, "viewer", { db });
110
+ }
84
111
  const query = args.query?.trim();
85
112
  const pattern = query ? `%${escapeLike(query.toLowerCase())}%` : null;
86
113
  const exactTitle = args.title?.toLowerCase();
@@ -160,6 +187,19 @@ export default defineAction({
160
187
  isNull(schema.documents.hideFromSearch),
161
188
  ),
162
189
  isNull(schema.contentDatabases.deletedAt),
190
+ isNull(schema.contentSpaces.archivedAt),
191
+ or(
192
+ isNull(schema.contentDatabases.spaceId),
193
+ and(
194
+ isNotNull(schema.contentSpaces.id),
195
+ or(
196
+ sql`LOWER(${schema.contentSpaces.ownerEmail}) = ${normalizedUserEmail}`,
197
+ authorizedOrganizationIds.length > 0
198
+ ? inArray(schema.contentSpaces.orgId, authorizedOrganizationIds)
199
+ : undefined,
200
+ ),
201
+ ),
202
+ ),
163
203
  isNull(schema.contentDatabases.systemRole),
164
204
  args.spaceId
165
205
  ? eq(schema.contentDatabases.spaceId, args.spaceId)
@@ -206,6 +246,10 @@ export default defineAction({
206
246
  schema.documents,
207
247
  eq(schema.contentDatabases.documentId, schema.documents.id),
208
248
  )
249
+ .leftJoin(
250
+ schema.contentSpaces,
251
+ eq(schema.contentDatabases.spaceId, schema.contentSpaces.id),
252
+ )
209
253
  .where(where)
210
254
  .orderBy(
211
255
  asc(schema.documents.position),
@@ -252,10 +296,51 @@ export default defineAction({
252
296
  schema.documents,
253
297
  eq(schema.contentDatabases.documentId, schema.documents.id),
254
298
  )
299
+ .leftJoin(
300
+ schema.contentSpaces,
301
+ eq(schema.contentDatabases.spaceId, schema.contentSpaces.id),
302
+ )
255
303
  .where(where)
256
304
  )[0]?.count ?? 0,
257
305
  );
258
306
 
307
+ const systemCollections = args.includeSystemCollections
308
+ ? await db
309
+ .select({
310
+ databaseId: schema.contentDatabases.id,
311
+ documentId: schema.contentDatabases.documentId,
312
+ title: schema.documents.title,
313
+ spaceId: schema.contentDatabases.spaceId,
314
+ spaceName: schema.contentSpaces.name,
315
+ spaceKind: schema.contentSpaces.kind,
316
+ systemRole: schema.contentDatabases.systemRole,
317
+ })
318
+ .from(schema.contentDatabases)
319
+ .innerJoin(
320
+ schema.documents,
321
+ eq(schema.contentDatabases.documentId, schema.documents.id),
322
+ )
323
+ .leftJoin(
324
+ schema.contentSpaces,
325
+ eq(schema.contentDatabases.spaceId, schema.contentSpaces.id),
326
+ )
327
+ .where(
328
+ and(
329
+ accessFilter(schema.documents, schema.documentShares),
330
+ isNull(schema.documents.trashedAt),
331
+ isNull(schema.contentDatabases.deletedAt),
332
+ isNull(schema.contentSpaces.archivedAt),
333
+ isNotNull(schema.contentSpaces.id),
334
+ isNotNull(schema.contentDatabases.systemRole),
335
+ eq(schema.contentDatabases.spaceId, args.spaceId!),
336
+ ),
337
+ )
338
+ .orderBy(
339
+ asc(schema.documents.position),
340
+ asc(schema.contentDatabases.id),
341
+ )
342
+ : undefined;
343
+
259
344
  return {
260
345
  databases,
261
346
  pagination: documentDiscoveryPagination({
@@ -264,6 +349,15 @@ export default defineAction({
264
349
  totalItems,
265
350
  returnedItems: databases.length,
266
351
  }),
352
+ ...(systemCollections
353
+ ? {
354
+ systemCollections: systemCollections.map((collection) => ({
355
+ ...collection,
356
+ title: collection.title ?? "Untitled database",
357
+ systemRole: collection.systemRole!,
358
+ })),
359
+ }
360
+ : {}),
267
361
  };
268
362
  },
269
363
  });
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-11
4
+ ---
5
+
6
+ Content mutations delegated from verified Slack DMs now use the member's exact Personal scope and return verified row receipts.
@@ -1082,6 +1082,16 @@ export interface ContentDatabaseSummary {
1082
1082
  description: string;
1083
1083
  }
1084
1084
 
1085
+ export interface ContentSystemCollectionSummary {
1086
+ databaseId: string;
1087
+ documentId: string;
1088
+ title: string;
1089
+ spaceId: string | null;
1090
+ spaceName: string | null;
1091
+ spaceKind: string | null;
1092
+ systemRole: string;
1093
+ }
1094
+
1085
1095
  export interface ContentDatabaseDescriptionResponse {
1086
1096
  database: ContentDatabase;
1087
1097
  contextPath: ContentContextPathEntry[];
@@ -1091,6 +1101,7 @@ export interface ContentDatabaseDescriptionResponse {
1091
1101
  export interface ListContentDatabasesResponse {
1092
1102
  databases: ContentDatabaseSummary[];
1093
1103
  pagination: DocumentDiscoveryPagination;
1104
+ systemCollections?: ContentSystemCollectionSummary[];
1094
1105
  }
1095
1106
 
1096
1107
  export interface TrashedContentDatabaseSummary {
@@ -63,7 +63,7 @@ decisions, feedback, agent runs, and provider audit records.
63
63
  | `evaluate-triage-item` | Append a decision. |
64
64
  | `record-triage-feedback` | Capture human correction for learning. |
65
65
  | `approve-factory-item` | Explicitly authorize one bounded run. |
66
- | `start-builder-for-item` | Govern clear-bug dispatch through Slack or Builder API. |
66
+ | `start-builder-for-item` | Govern clear-bug dispatch through Slack or Builder API, or record a skip reason. |
67
67
  | `govern-agent-native-pull-request` | Apply PR evidence and ownership gates. |
68
68
  | `list-factory-automations` / `save-factory-automation` / `run-factory-automation` | Inspect or edit org-owned automations. |
69
69
  | `list-factory-audit` | Inspect automation runs, evidence, decisions, and provider actions. |
@@ -41,6 +41,10 @@ export default defineAction({
41
41
  timezone: meta.timezone ?? null,
42
42
  condition: meta.condition ?? null,
43
43
  createdBy: meta.createdBy ?? null,
44
+ updatedAt:
45
+ Number.isFinite(resource.updatedAt) && resource.updatedAt > 0
46
+ ? new Date(resource.updatedAt).toISOString()
47
+ : null,
44
48
  canUpdate,
45
49
  runs: await listAutomationRuns({
46
50
  owners: [resource.owner],