@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
@@ -2062,11 +2062,19 @@ export function App() {
2062
2062
  const controller = new AbortController();
2063
2063
  const abortTimer = setTimeout(() => controller.abort(), POLL_ABORT_MS);
2064
2064
  try {
2065
- const exchangeParams = new URLSearchParams({ flow_id: flowId });
2066
- if (verifier) exchangeParams.set("verifier", verifier);
2067
2065
  const xr = await fetch(
2068
- `${base}/_agent-native/auth/desktop-exchange?${exchangeParams.toString()}`,
2069
- { credentials: "include", signal: controller.signal },
2066
+ `${base}/_agent-native/auth/desktop-exchange?flow_id=${encodeURIComponent(flowId)}`,
2067
+ {
2068
+ credentials: "include",
2069
+ ...(verifier
2070
+ ? {
2071
+ headers: {
2072
+ "X-Agent-Native-Desktop-Verifier": verifier,
2073
+ },
2074
+ }
2075
+ : {}),
2076
+ signal: controller.signal,
2077
+ },
2070
2078
  );
2071
2079
  if (!xr.ok) {
2072
2080
  if (Date.now() - start > TIMEOUT_MS) {
@@ -2130,25 +2138,79 @@ export function App() {
2130
2138
  void tick();
2131
2139
  }
2132
2140
 
2133
- // Google and magic-link verification open in the system browser because
2134
- // the Tauri WebView has its own cookie jar. Both flows return through the
2135
- // same short-lived server-side exchange.
2141
+ // Google verification stays in the bound Tauri WebView so the callback sees
2142
+ // the same browser-binding cookie. Magic-link verification still completes
2143
+ // in the system browser through its own exchange flow.
2136
2144
  async function signInExternal() {
2137
2145
  if (signInInflightRef.current) return;
2138
2146
  signInInflightRef.current = true;
2139
2147
 
2140
2148
  try {
2141
2149
  setSignInError(null);
2142
- const flowId =
2143
- crypto.randomUUID?.() ||
2144
- Math.random().toString(36).slice(2) + Date.now().toString(36);
2150
+ const flowId = crypto.randomUUID?.() ?? null;
2151
+ const verifier = (() => {
2152
+ const randomUuid = crypto.randomUUID;
2153
+ if (typeof randomUuid === "function") {
2154
+ return `${randomUuid.call(crypto)}${randomUuid.call(crypto)}`;
2155
+ }
2156
+ if (typeof crypto.getRandomValues === "function") {
2157
+ const bytes = new Uint8Array(32);
2158
+ crypto.getRandomValues(bytes);
2159
+ let binary = "";
2160
+ for (const byte of bytes) binary += String.fromCharCode(byte);
2161
+ return btoa(binary)
2162
+ .replace(/\+/g, "-")
2163
+ .replace(/\//g, "_")
2164
+ .replace(/=+$/, "");
2165
+ }
2166
+ return null;
2167
+ })();
2168
+ if (!flowId || !verifier) {
2169
+ // A Math.random flow id is guessable, which lets anyone else claim this
2170
+ // sign-in's exchange slot; fail closed rather than weaken the credential.
2171
+ throw new Error("Secure OAuth flow generation is unavailable.");
2172
+ }
2145
2173
  const base = serverUrl.replace(/\/+$/, "");
2146
2174
 
2147
- await openExternal(
2148
- `${base}/_agent-native/google/auth-url?desktop=1&flow_id=${flowId}&redirect=1`,
2175
+ const authParams = new URLSearchParams({
2176
+ desktop: "1",
2177
+ flow_id: flowId,
2178
+ webview: "1",
2179
+ });
2180
+ const authResponse = await fetch(
2181
+ `${base}/_agent-native/google/auth-url?${authParams.toString()}`,
2182
+ {
2183
+ method: "POST",
2184
+ headers: {
2185
+ Accept: "application/json",
2186
+ "X-Agent-Native-Desktop-Verifier": verifier,
2187
+ },
2188
+ },
2149
2189
  );
2190
+ let authPayload: {
2191
+ url?: unknown;
2192
+ error?: unknown;
2193
+ message?: unknown;
2194
+ };
2195
+ try {
2196
+ authPayload = (await authResponse.json()) as typeof authPayload;
2197
+ } catch {
2198
+ throw new Error("Could not start Google sign-in.");
2199
+ }
2200
+ if (!authResponse.ok || typeof authPayload?.url !== "string") {
2201
+ const message =
2202
+ typeof authPayload.message === "string"
2203
+ ? authPayload.message
2204
+ : typeof authPayload.error === "string"
2205
+ ? authPayload.error
2206
+ : "Could not start Google sign-in.";
2207
+ throw new Error(message);
2208
+ }
2150
2209
  setSignInPending("google");
2151
- startDesktopAuthExchange(flowId, "google");
2210
+ // Stay in this exact WebView: the auth bootstrap set the HttpOnly
2211
+ // browser-binding cookie here, and the callback returns this page with
2212
+ // the staged session cookie after Google completes.
2213
+ window.location.href = authPayload.url;
2152
2214
  } catch (err) {
2153
2215
  console.error("[clips-tray] signInExternal failed:", err);
2154
2216
  signInInflightRef.current = false;
@@ -3912,8 +3974,8 @@ export function App() {
3912
3974
  // (not a separate Tauri window). This avoids Tauri 2's separate-WebKit-
3913
3975
  // data-store-per-WebviewWindow cookie-jar issue — the cookie is set in
3914
3976
  // the same webview that reads it on the next /auth/session poll.
3915
- // Google and magic-link verification use the system browser, while the
3916
- // password fallback stays inline in this WebView.
3977
+ // Google verification uses the bound WebView, while magic-link verification
3978
+ // uses the system browser and password stays inline here.
3917
3979
  if (authStatus === "anon") {
3918
3980
  return (
3919
3981
  <div className="app" ref={appRef}>
@@ -4822,7 +4884,7 @@ function SignInForm({
4822
4884
  type="button"
4823
4885
  className="signin-google"
4824
4886
  onClick={onUseBrowser}
4825
- title="Opens your default browser to complete Google sign-in"
4887
+ title="Returns to Clips to complete Google sign-in"
4826
4888
  >
4827
4889
  <GoogleIcon />
4828
4890
  Sign in with Google
@@ -15,8 +15,9 @@ crate-type = ["lib", "cdylib", "staticlib"]
15
15
 
16
16
  # Force the dev binary name so macOS shows "Clips" in the Dock / app switcher
17
17
  # (macOS derives the display name from the executable filename when the app
18
- # is unbundled — e.g. during `tauri dev`). Bundled .app builds read the name
19
- # from `productName` in tauri.conf.json instead.
18
+ # is unbundled — e.g. during `tauri dev`). The Nightly release workflow
19
+ # rewrites this target and tauri.conf.json's mainBinaryName to
20
+ # `Clips-Nightly` before packaging so both lanes can coexist.
20
21
  [[bin]]
21
22
  name = "Clips"
22
23
  path = "src/main.rs"
@@ -2,6 +2,7 @@
2
2
  "$schema": "https://schema.tauri.app/config/2",
3
3
  "productName": "Clips",
4
4
  "version": "0.1.1",
5
+ "mainBinaryName": "Clips",
5
6
  "identifier": "com.clips.tray",
6
7
  "build": {
7
8
  "beforeDevCommand": "pnpm vite:dev",
@@ -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",
@@ -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. |
@@ -71,7 +71,8 @@ decisions, feedback, agent runs, and provider audit records.
71
71
  | `suggest-factory-rules` | Mine feedback into proposals. |
72
72
  | `reconcile-triage-run` | Persist callback/provider reconciliation. |
73
73
  | `list-factories` / `get-factory-graph` | Inspect definitions, versions, and metrics. |
74
- | `save-factory-graph` | Create or version a complete visual graph; never starts provider work. |
74
+ | `save-factory-graph` | Create/version a graph with inspected `expectedGraphVersion`; never starts provider work. |
75
+ | graph history actions | Factory graph version history. |
75
76
  | `list-factory-comments` / `add-factory-comment` | Read or attach comments to a canvas, node, or edge. |
76
77
  | `provider-api-catalog` / `provider-api-docs` / `provider-api-request` | Use connected provider APIs with shared credentials; never request raw keys. |
77
78
  | `list-workspace-apps` / `update-workspace-app-metadata` | Inventory and edit mounted apps. |
@@ -0,0 +1,69 @@
1
+ import { defineAction } from "@agent-native/core/action";
2
+ import { and, eq } from "drizzle-orm";
3
+ import { z } from "zod";
4
+
5
+ import { getDb } from "../server/db/index.js";
6
+ import { factoryGraphVersions } from "../server/db/schema.js";
7
+ import {
8
+ DEFAULT_FACTORY_ID,
9
+ parseFactoryGraph,
10
+ readFactoryDefinition,
11
+ } from "../server/factory-graph/store.js";
12
+ import {
13
+ requireWorkspaceMember,
14
+ workspaceMemberIdentityFromContext,
15
+ } from "../server/lib/require-workspace-member.js";
16
+
17
+ export default defineAction({
18
+ description:
19
+ "Read one validated Factory visual graph version for history preview. The snapshot is scoped to the active workspace and does not change the current Factory.",
20
+ schema: z.object({
21
+ factoryId: z
22
+ .string()
23
+ .trim()
24
+ .min(1)
25
+ .max(120)
26
+ .regex(/^[a-z0-9][a-z0-9-]*$/)
27
+ .default(DEFAULT_FACTORY_ID),
28
+ versionId: z.string().trim().min(1).max(240),
29
+ }),
30
+ http: { method: "GET" },
31
+ readOnly: true,
32
+ parallelSafe: true,
33
+ run: async ({ factoryId, versionId }, context) => {
34
+ const { orgId } = await requireWorkspaceMember(
35
+ workspaceMemberIdentityFromContext(context),
36
+ );
37
+ const currentDefinition = await readFactoryDefinition(orgId, factoryId);
38
+ const currentVersion = currentDefinition?.graphVersion ?? null;
39
+ const row = (
40
+ await getDb()
41
+ .select()
42
+ .from(factoryGraphVersions)
43
+ .where(
44
+ and(
45
+ eq(factoryGraphVersions.id, versionId),
46
+ eq(factoryGraphVersions.factoryId, factoryId),
47
+ eq(factoryGraphVersions.orgId, orgId),
48
+ ),
49
+ )
50
+ .limit(1)
51
+ )[0];
52
+
53
+ if (!row) {
54
+ throw new Error("Factory graph version not found.");
55
+ }
56
+
57
+ return {
58
+ id: row.id,
59
+ factoryId: row.factoryId,
60
+ version: row.version,
61
+ graph: parseFactoryGraph(row.graphJson),
62
+ source: row.source,
63
+ changeSummary: row.changeSummary,
64
+ createdAt: row.createdAt,
65
+ createdBy: row.createdBy,
66
+ isCurrent: currentVersion === row.version,
67
+ };
68
+ },
69
+ });
@@ -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],
@@ -0,0 +1,82 @@
1
+ import { defineAction } from "@agent-native/core/action";
2
+ import { and, desc, eq, lt } from "drizzle-orm";
3
+ import { z } from "zod";
4
+
5
+ import { getDb } from "../server/db/index.js";
6
+ import { factoryGraphVersions } from "../server/db/schema.js";
7
+ import {
8
+ DEFAULT_FACTORY_ID,
9
+ readFactoryDefinition,
10
+ } from "../server/factory-graph/store.js";
11
+ import {
12
+ requireWorkspaceMember,
13
+ workspaceMemberIdentityFromContext,
14
+ } from "../server/lib/require-workspace-member.js";
15
+
16
+ export default defineAction({
17
+ description:
18
+ "List bounded saved visual graph version metadata for a Factory, newest first. Use get-factory-graph-version to read one validated snapshot for preview or restore.",
19
+ schema: z.object({
20
+ factoryId: z
21
+ .string()
22
+ .trim()
23
+ .min(1)
24
+ .max(120)
25
+ .regex(/^[a-z0-9][a-z0-9-]*$/)
26
+ .default(DEFAULT_FACTORY_ID),
27
+ limit: z.coerce.number().int().min(1).max(50).default(25),
28
+ beforeVersion: z.coerce.number().int().positive().optional(),
29
+ }),
30
+ http: { method: "GET" },
31
+ readOnly: true,
32
+ parallelSafe: true,
33
+ run: async ({ factoryId, limit, beforeVersion }, context) => {
34
+ const { orgId } = await requireWorkspaceMember(
35
+ workspaceMemberIdentityFromContext(context),
36
+ );
37
+ const currentDefinition = await readFactoryDefinition(orgId, factoryId);
38
+ const currentVersion = currentDefinition?.graphVersion ?? null;
39
+ const rows = await getDb()
40
+ .select({
41
+ id: factoryGraphVersions.id,
42
+ factoryId: factoryGraphVersions.factoryId,
43
+ version: factoryGraphVersions.version,
44
+ source: factoryGraphVersions.source,
45
+ changeSummary: factoryGraphVersions.changeSummary,
46
+ createdAt: factoryGraphVersions.createdAt,
47
+ createdBy: factoryGraphVersions.createdBy,
48
+ })
49
+ .from(factoryGraphVersions)
50
+ .where(
51
+ and(
52
+ eq(factoryGraphVersions.factoryId, factoryId),
53
+ eq(factoryGraphVersions.orgId, orgId),
54
+ beforeVersion === undefined
55
+ ? undefined
56
+ : lt(factoryGraphVersions.version, beforeVersion),
57
+ ),
58
+ )
59
+ .orderBy(desc(factoryGraphVersions.version))
60
+ .limit(limit + 1);
61
+ const page = rows.slice(0, limit);
62
+ const hasMore = rows.length > limit;
63
+
64
+ return {
65
+ factoryId,
66
+ currentVersion,
67
+ hasMore,
68
+ nextBeforeVersion:
69
+ hasMore && page.length > 0 ? page[page.length - 1].version : null,
70
+ versions: page.map((row) => ({
71
+ id: row.id,
72
+ factoryId: row.factoryId,
73
+ version: row.version,
74
+ source: row.source,
75
+ changeSummary: row.changeSummary,
76
+ createdAt: row.createdAt,
77
+ createdBy: row.createdBy,
78
+ isCurrent: currentVersion === row.version,
79
+ })),
80
+ };
81
+ },
82
+ });