@agent-native/core 0.95.0 → 0.95.1

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 (135) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +10 -0
  3. package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +13 -0
  4. package/corpus/core/docs/content/locales/de-DE/template-design.mdx +18 -0
  5. package/corpus/core/docs/content/locales/es-ES/template-design.mdx +16 -0
  6. package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +17 -0
  7. package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +16 -0
  8. package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +17 -0
  9. package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +15 -0
  10. package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +16 -0
  11. package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +11 -0
  12. package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +12 -0
  13. package/corpus/core/docs/content/template-design.mdx +16 -0
  14. package/corpus/core/package.json +2 -2
  15. package/corpus/core/src/agent/production-agent.ts +20 -0
  16. package/corpus/core/src/agent/thread-data-builder.ts +41 -17
  17. package/corpus/core/src/client/AssistantChat.tsx +250 -25
  18. package/corpus/core/src/client/chat/tool-call-display.tsx +1 -1
  19. package/corpus/core/src/client/chat/use-reconnect-reader-owner.ts +28 -0
  20. package/corpus/core/src/client/guided-questions.tsx +9 -1
  21. package/corpus/core/src/client/integrations/IntegrationsPanel.tsx +6 -7
  22. package/corpus/core/src/client/sse-event-processor.ts +90 -21
  23. package/corpus/core/src/connections/catalog.ts +2 -2
  24. package/corpus/core/src/deploy/build.ts +3 -0
  25. package/corpus/core/src/integrations/catalog.ts +2 -2
  26. package/corpus/core/src/server/auth.ts +1 -1
  27. package/corpus/core/src/server/credential-provider.ts +7 -3
  28. package/corpus/core/src/shared/cache-control.ts +10 -4
  29. package/corpus/templates/analytics/app/lib/data-sources.ts +5 -4
  30. package/corpus/templates/analytics/changelog/2026-07-10-slack-token-setup-marked-legacy.md +6 -0
  31. package/corpus/templates/analytics/server/lib/credential-keys.ts +6 -2
  32. package/corpus/templates/brain/changelog/2026-07-10-slack-backfill-token-marked-legacy.md +6 -0
  33. package/corpus/templates/brain/server/register-secrets.ts +2 -2
  34. package/corpus/templates/clips/app/components/library/library-layout.tsx +7 -1
  35. package/corpus/templates/clips/app/components/library/page-header.tsx +19 -3
  36. package/corpus/templates/clips/app/routes/_app.library._index.tsx +16 -12
  37. package/corpus/templates/clips/changelog/2026-07-10-new-recording-appears-once-when-the-library-sidebar-is-open.md +6 -0
  38. package/corpus/templates/clips/changelog/2026-07-10-slack-token-fallback-guidance.md +6 -0
  39. package/corpus/templates/clips/server/register-secrets.ts +1 -1
  40. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +0 -66
  41. package/corpus/templates/content/app/routes/_app.local-files.tsx +7 -47
  42. package/corpus/templates/content/changelog/2026-07-10-local-folders-now-wait-for-an-explicit-pull-before-content-r.md +6 -0
  43. package/corpus/templates/content/package.json +1 -1
  44. package/corpus/templates/design/actions/generate-design.ts +128 -31
  45. package/corpus/templates/design/actions/present-design-variants.ts +37 -9
  46. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +9 -3
  47. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +32 -10
  48. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +2 -2
  49. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +9 -1
  50. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +7 -5
  51. package/corpus/templates/design/app/components/design/multi-screen/types.ts +3 -0
  52. package/corpus/templates/design/app/global.css +10 -0
  53. package/corpus/templates/design/app/hooks/use-question-flow.ts +9 -2
  54. package/corpus/templates/design/app/i18n/zh-TW.ts +1 -0
  55. package/corpus/templates/design/app/i18n-data.ts +21 -11
  56. package/corpus/templates/design/changelog/2026-07-10-editor-panels-are-easier-to-distinguish-and-design-intake-qu.md +6 -0
  57. package/corpus/templates/design/changelog/2026-07-10-interact-mode-now-stays-on-the-full-canvas-matches-edit-visu.md +6 -0
  58. package/corpus/templates/design/changelog/2026-07-10-new-web-designs-open-at-desktop-size-and-adapt-across-mobile.md +6 -0
  59. package/corpus/templates/design/changelog/2026-07-10-tweaks-now-link-to-full-documentation.md +6 -0
  60. package/corpus/templates/design/server/plugins/agent-chat.ts +2 -0
  61. package/corpus/templates/mail/app/i18n/ar-SA.ts +3 -3
  62. package/corpus/templates/mail/app/i18n/de-DE.ts +3 -3
  63. package/corpus/templates/mail/app/i18n/en-US.ts +4 -3
  64. package/corpus/templates/mail/app/i18n/es-ES.ts +3 -3
  65. package/corpus/templates/mail/app/i18n/fr-FR.ts +3 -3
  66. package/corpus/templates/mail/app/i18n/hi-IN.ts +3 -3
  67. package/corpus/templates/mail/app/i18n/ja-JP.ts +3 -3
  68. package/corpus/templates/mail/app/i18n/ko-KR.ts +3 -3
  69. package/corpus/templates/mail/app/i18n/pt-BR.ts +3 -3
  70. package/corpus/templates/mail/app/i18n/zh-CN.ts +3 -3
  71. package/corpus/templates/mail/app/i18n/zh-TW.ts +3 -3
  72. package/corpus/templates/mail/changelog/2026-07-10-slack-intake-marked-legacy.md +6 -0
  73. package/corpus/templates/mail/server/lib/env-config.ts +5 -4
  74. package/corpus/templates/plan/package.json +1 -1
  75. package/dist/agent/production-agent.d.ts.map +1 -1
  76. package/dist/agent/production-agent.js +20 -0
  77. package/dist/agent/production-agent.js.map +1 -1
  78. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  79. package/dist/agent/thread-data-builder.js +36 -18
  80. package/dist/agent/thread-data-builder.js.map +1 -1
  81. package/dist/client/AssistantChat.d.ts +3 -1
  82. package/dist/client/AssistantChat.d.ts.map +1 -1
  83. package/dist/client/AssistantChat.js +200 -28
  84. package/dist/client/AssistantChat.js.map +1 -1
  85. package/dist/client/chat/tool-call-display.js +1 -1
  86. package/dist/client/chat/tool-call-display.js.map +1 -1
  87. package/dist/client/chat/use-reconnect-reader-owner.d.ts +11 -0
  88. package/dist/client/chat/use-reconnect-reader-owner.d.ts.map +1 -0
  89. package/dist/client/chat/use-reconnect-reader-owner.js +21 -0
  90. package/dist/client/chat/use-reconnect-reader-owner.js.map +1 -0
  91. package/dist/client/guided-questions.d.ts.map +1 -1
  92. package/dist/client/guided-questions.js +5 -1
  93. package/dist/client/guided-questions.js.map +1 -1
  94. package/dist/client/integrations/IntegrationsPanel.d.ts.map +1 -1
  95. package/dist/client/integrations/IntegrationsPanel.js +5 -7
  96. package/dist/client/integrations/IntegrationsPanel.js.map +1 -1
  97. package/dist/client/sse-event-processor.d.ts.map +1 -1
  98. package/dist/client/sse-event-processor.js +67 -20
  99. package/dist/client/sse-event-processor.js.map +1 -1
  100. package/dist/collab/routes.d.ts +1 -1
  101. package/dist/connections/catalog.d.ts +2 -2
  102. package/dist/connections/catalog.js +2 -2
  103. package/dist/connections/catalog.js.map +1 -1
  104. package/dist/deploy/build.d.ts.map +1 -1
  105. package/dist/deploy/build.js +3 -1
  106. package/dist/deploy/build.js.map +1 -1
  107. package/dist/integrations/catalog.js +2 -2
  108. package/dist/integrations/catalog.js.map +1 -1
  109. package/dist/notifications/routes.d.ts +2 -2
  110. package/dist/observability/routes.d.ts +1 -1
  111. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  112. package/dist/resources/handlers.d.ts +1 -1
  113. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  114. package/dist/server/auth.js +1 -1
  115. package/dist/server/auth.js.map +1 -1
  116. package/dist/server/credential-provider.d.ts +5 -3
  117. package/dist/server/credential-provider.d.ts.map +1 -1
  118. package/dist/server/credential-provider.js +7 -3
  119. package/dist/server/credential-provider.js.map +1 -1
  120. package/dist/shared/cache-control.d.ts +8 -6
  121. package/dist/shared/cache-control.d.ts.map +1 -1
  122. package/dist/shared/cache-control.js +8 -3
  123. package/dist/shared/cache-control.js.map +1 -1
  124. package/docs/content/locales/ar-SA/template-design.mdx +13 -0
  125. package/docs/content/locales/de-DE/template-design.mdx +18 -0
  126. package/docs/content/locales/es-ES/template-design.mdx +16 -0
  127. package/docs/content/locales/fr-FR/template-design.mdx +17 -0
  128. package/docs/content/locales/hi-IN/template-design.mdx +16 -0
  129. package/docs/content/locales/ja-JP/template-design.mdx +17 -0
  130. package/docs/content/locales/ko-KR/template-design.mdx +15 -0
  131. package/docs/content/locales/pt-BR/template-design.mdx +16 -0
  132. package/docs/content/locales/zh-CN/template-design.mdx +11 -0
  133. package/docs/content/locales/zh-TW/template-design.mdx +12 -0
  134. package/docs/content/template-design.mdx +16 -0
  135. package/package.json +2 -2
@@ -32,6 +32,7 @@ import { normalizeAppBasePath } from "../server/app-base-path.js";
32
32
  import {
33
33
  DEFAULT_SSR_CDN_CACHE_CONTROL,
34
34
  DEFAULT_SSR_NETLIFY_CDN_CACHE_CONTROL,
35
+ DEFAULT_SSR_NETLIFY_VARY,
35
36
  DEFAULT_SPECULATION_RULES_PATH,
36
37
  DEFAULT_SSR_CACHE_CONTROL,
37
38
  } from "../shared/cache-control.js";
@@ -908,6 +909,7 @@ function injectHeadScript(html, script) {
908
909
  const DEFAULT_SSR_CACHE_CONTROL = ${JSON.stringify(DEFAULT_SSR_CACHE_CONTROL)};
909
910
  const DEFAULT_SSR_CDN_CACHE_CONTROL = ${JSON.stringify(DEFAULT_SSR_CDN_CACHE_CONTROL)};
910
911
  const DEFAULT_SSR_NETLIFY_CDN_CACHE_CONTROL = ${JSON.stringify(DEFAULT_SSR_NETLIFY_CDN_CACHE_CONTROL)};
912
+ const DEFAULT_SSR_NETLIFY_VARY = ${JSON.stringify(DEFAULT_SSR_NETLIFY_VARY)};
911
913
  const DEFAULT_SPECULATION_RULES_PATH = ${JSON.stringify(DEFAULT_SPECULATION_RULES_PATH)};
912
914
  const IMMUTABLE_ASSET_CACHE_CONTROL = ${JSON.stringify(IMMUTABLE_ASSET_CACHE_CONTROL)};
913
915
  const IMMUTABLE_ASSET_PATHS = new Set(${JSON.stringify(
@@ -1003,6 +1005,7 @@ function applyDefaultSsrCacheHeader(headers, status, pathname) {
1003
1005
  // Netlify-specific header so SSR HTML/.data are served from the shared
1004
1006
  // durable CDN cache instead of stampeding origin — for every visitor.
1005
1007
  headers.set("netlify-cdn-cache-control", DEFAULT_SSR_NETLIFY_CDN_CACHE_CONTROL);
1008
+ headers.set("netlify-vary", DEFAULT_SSR_NETLIFY_VARY);
1006
1009
  }
1007
1010
 
1008
1011
  function applyDefaultSpeculationRulesHeader(headers, status, basePath) {
@@ -137,10 +137,10 @@ const BUILT_IN_CHANNEL_CATALOG = [
137
137
  credentialRequirements: [
138
138
  {
139
139
  key: "SLACK_BOT_TOKEN",
140
- label: "Slack Bot Token",
140
+ label: "Slack Bot Token (legacy)",
141
141
  required: false,
142
142
  helpText:
143
- "OAuth & PermissionsBot User OAuth Token (starts with `xoxb-`).",
143
+ "Legacy single-workspace fallback only. For new setups, install Slack from Settings Messaging; managed OAuth stores each workspace token automatically.",
144
144
  },
145
145
  {
146
146
  key: "SLACK_CLIENT_ID",
@@ -1627,7 +1627,7 @@ function loginHtmlResponse(loginHtml: string, event: H3Event): Response {
1627
1627
  headers: {
1628
1628
  "Content-Type": "text/html; charset=utf-8",
1629
1629
  // The sign-in document is part of the public server shell. Keep it on the
1630
- // same short-fresh/long-SWR CDN policy as React Router SSR so hosted
1630
+ // same long-fresh/long-SWR CDN policy as React Router SSR so hosted
1631
1631
  // template roots do not invoke origin just to render anonymous login UI.
1632
1632
  // The login HTML is env-INDEPENDENT (a Google-only app always renders a
1633
1633
  // working button), so a cached copy is never "wrong" — never downgrade
@@ -78,6 +78,8 @@ const APP_PROVIDED_DEPLOY_CREDENTIAL_KEYS = new Set([
78
78
  "OPENAI_API_KEY",
79
79
  "OPENAI_BASE_URL",
80
80
  "OPENROUTER_API_KEY",
81
+ "GOOGLE_CLIENT_ID",
82
+ "GOOGLE_CLIENT_SECRET",
81
83
  "GOOGLE_GENERATIVE_AI_API_KEY",
82
84
  "GROQ_API_KEY",
83
85
  "MISTRAL_API_KEY",
@@ -93,9 +95,11 @@ function isAppProvidedDeployCredentialKey(key: string | undefined): boolean {
93
95
  * single-tenant contexts. In hosted production with a shared database, every
94
96
  * signed-in user needs their own user/org/workspace credential for
95
97
  * identity-bearing provider keys so one deploy key does not silently
96
- * impersonate another tenant. App-provided LLM provider keys are different:
97
- * they intentionally let the app developer pay for model usage for users of
98
- * that deployed app, so key-aware callers may use those env vars.
98
+ * impersonate another tenant. App-provided service credentials are different:
99
+ * they configure the deployed app itself rather than identifying a user. This
100
+ * includes LLM keys that let the app developer pay for model usage and OAuth
101
+ * client credentials whose per-user identity remains in scoped OAuth tokens.
102
+ * Key-aware callers may use those env vars.
99
103
  *
100
104
  * @deprecated Use `canUseDeployCredentialFallbackForRequest()` for generic
101
105
  * provider secrets. This stricter helper remains for legacy call sites with
@@ -1,15 +1,21 @@
1
- export const DEFAULT_SSR_CACHE_CONTROL =
2
- "public, max-age=5, stale-while-revalidate=604800, stale-if-error=3600";
1
+ export const DEFAULT_SSR_CACHE_CONTROL = "public, max-age=0, must-revalidate";
3
2
 
4
- export const DEFAULT_SSR_CDN_CACHE_CONTROL = DEFAULT_SSR_CACHE_CONTROL;
3
+ export const DEFAULT_SSR_CDN_CACHE_CONTROL =
4
+ "public, s-maxage=3600, stale-while-revalidate=604800, stale-if-error=3600";
5
5
 
6
6
  export const DEFAULT_SSR_NETLIFY_CDN_CACHE_CONTROL =
7
- "public, durable, max-age=5, stale-while-revalidate=604800, stale-if-error=3600";
7
+ "public, durable, s-maxage=3600, stale-while-revalidate=604800, stale-if-error=3600";
8
+
9
+ // React Router data requests can select a subset of route loaders with
10
+ // `?_routes=...`. Ignore every other query parameter (UTM tags, click ids,
11
+ // and similar tracking noise) so they cannot fragment Netlify's SSR cache.
12
+ export const DEFAULT_SSR_NETLIFY_VARY = "query=_routes";
8
13
 
9
14
  export const DEFAULT_SSR_CACHE_HEADERS = {
10
15
  "cache-control": DEFAULT_SSR_CACHE_CONTROL,
11
16
  "cdn-cache-control": DEFAULT_SSR_CDN_CACHE_CONTROL,
12
17
  "netlify-cdn-cache-control": DEFAULT_SSR_NETLIFY_CDN_CACHE_CONTROL,
18
+ "netlify-vary": DEFAULT_SSR_NETLIFY_VARY,
13
19
  } as const;
14
20
 
15
21
  export const DEFAULT_SPECULATION_RULES_PATH =
@@ -748,14 +748,15 @@ export const dataSources: DataSource[] = [
748
748
  {
749
749
  id: "slack",
750
750
  name: "Slack",
751
- description: "Channel messages and workspace search",
751
+ description:
752
+ "Channel history and workspace search. Prefer a Slack workspace integration from Settings > Connections; a local bot token remains available as a legacy fallback.",
752
753
  category: "communication",
753
754
  icon: IconMessage,
754
755
  envKeys: ["SLACK_BOT_TOKEN"],
755
756
  docsUrl: "https://api.slack.com/methods",
756
757
  walkthroughSteps: [
757
758
  {
758
- title: "Create a Slack App",
759
+ title: "Create a Slack App for local fallback access",
759
760
  description:
760
761
  "Go to api.slack.com/apps, create a new app, and add OAuth scopes: channels:read, channels:history, search:read.",
761
762
  url: "https://api.slack.com/apps",
@@ -767,10 +768,10 @@ export const dataSources: DataSource[] = [
767
768
  'Under "OAuth & Permissions", install the app and copy the Bot User OAuth Token.',
768
769
  },
769
770
  {
770
- title: "Enter your Bot Token",
771
+ title: "Enter your local fallback Bot Token",
771
772
  description: 'Paste the Bot User OAuth Token (starts with "xoxb-").',
772
773
  inputKey: "SLACK_BOT_TOKEN",
773
- inputLabel: "Bot Token",
774
+ inputLabel: "Bot Token (legacy local fallback)",
774
775
  inputPlaceholder: "xoxb-...",
775
776
  inputType: "password",
776
777
  },
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-10
4
+ ---
5
+
6
+ Slack data-source setup now favors workspace connections and identifies raw bot tokens as a legacy local fallback.
@@ -107,10 +107,14 @@ export const credentialKeys: CredentialKeyConfig[] = [
107
107
  required: false,
108
108
  },
109
109
  // Slack
110
- { key: "SLACK_BOT_TOKEN", label: "Slack Bot Token", required: false },
110
+ {
111
+ key: "SLACK_BOT_TOKEN",
112
+ label: "Slack Bot Token (legacy local fallback)",
113
+ required: false,
114
+ },
111
115
  {
112
116
  key: "SLACK_BOT_TOKEN_2",
113
- label: "Slack Bot Token (secondary)",
117
+ label: "Slack Bot Token (legacy secondary fallback)",
114
118
  required: false,
115
119
  },
116
120
  // Notion
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-10
4
+ ---
5
+
6
+ Slack backfills now favor workspace connections while clearly marking raw bot tokens as a local fallback.
@@ -2,9 +2,9 @@ import { registerRequiredSecret } from "@agent-native/core/secrets";
2
2
 
3
3
  registerRequiredSecret({
4
4
  key: "SLACK_BOT_TOKEN",
5
- label: "Slack Bot Token",
5
+ label: "Slack Bot Token (local fallback)",
6
6
  description:
7
- "Optional Slack bot token for channel backfills. Brain only scans allow-listed channels and never enumerates DMs.",
7
+ "Optional local fallback for Brain channel backfills. Prefer a Slack workspace integration in Settings > Connections. Brain only scans allow-listed channels and never enumerates DMs.",
8
8
  docsUrl: "https://api.slack.com/authentication/token-types",
9
9
  scope: "workspace",
10
10
  kind: "api-key",
@@ -154,6 +154,9 @@ export function LibraryLayout({ children }: LibraryLayoutProps) {
154
154
  );
155
155
  const [headerSlot, setHeaderSlot] = useState<HTMLElement | null>(null);
156
156
  const showCollapsedSidebar = sidebarCollapsed && !isMobile;
157
+ const sidebarHasNewRecordingAction = isMobile
158
+ ? sidebarOpen
159
+ : !sidebarCollapsed;
157
160
 
158
161
  // Routes whose page renders its own h-12 toolbar. Layout still mounts Sidebar
159
162
  // + AgentSidebar, but skips its own header so there's no double-header.
@@ -603,7 +606,10 @@ export function LibraryLayout({ children }: LibraryLayoutProps) {
603
606
  </div>
604
607
  )}
605
608
  <main className="agent-native-app-main flex min-h-0 flex-1 flex-col overflow-y-auto">
606
- <PageHeaderSlotProvider slot={headerSlot}>
609
+ <PageHeaderSlotProvider
610
+ slot={headerSlot}
611
+ sidebarHasNewRecordingAction={sidebarHasNewRecordingAction}
612
+ >
607
613
  {children}
608
614
  </PageHeaderSlotProvider>
609
615
  </main>
@@ -7,24 +7,40 @@ import {
7
7
  } from "react";
8
8
  import { createPortal } from "react-dom";
9
9
 
10
- const PageHeaderSlotContext = createContext<HTMLElement | null>(null);
10
+ interface PageHeaderSlotContextValue {
11
+ slot: HTMLElement | null;
12
+ sidebarHasNewRecordingAction: boolean;
13
+ }
14
+
15
+ const PageHeaderSlotContext = createContext<PageHeaderSlotContextValue>({
16
+ slot: null,
17
+ sidebarHasNewRecordingAction: false,
18
+ });
11
19
 
12
20
  export function PageHeaderSlotProvider({
13
21
  slot,
22
+ sidebarHasNewRecordingAction,
14
23
  children,
15
24
  }: {
16
25
  slot: HTMLElement | null;
26
+ sidebarHasNewRecordingAction: boolean;
17
27
  children: ReactNode;
18
28
  }) {
19
29
  return (
20
- <PageHeaderSlotContext.Provider value={slot}>
30
+ <PageHeaderSlotContext.Provider
31
+ value={{ slot, sidebarHasNewRecordingAction }}
32
+ >
21
33
  {children}
22
34
  </PageHeaderSlotContext.Provider>
23
35
  );
24
36
  }
25
37
 
38
+ export function usePageHeaderLayout() {
39
+ return useContext(PageHeaderSlotContext);
40
+ }
41
+
26
42
  export function PageHeader({ children }: { children: ReactNode }) {
27
- const slot = useContext(PageHeaderSlotContext);
43
+ const { slot } = usePageHeaderLayout();
28
44
  const [ready, setReady] = useState(false);
29
45
  useEffect(() => setReady(true), []);
30
46
  if (!ready || !slot) return null;
@@ -4,6 +4,7 @@ import { IconPlayerRecord } from "@tabler/icons-react";
4
4
  import { NavLink } from "react-router";
5
5
 
6
6
  import { LibraryGrid } from "@/components/library/library-grid";
7
+ import { usePageHeaderLayout } from "@/components/library/page-header";
7
8
 
8
9
  const SEO_TITLE =
9
10
  "Agent-Native Clips - Open Source, agent-friendly Loom alternative";
@@ -24,24 +25,27 @@ export function meta() {
24
25
 
25
26
  export default function LibraryIndexRoute() {
26
27
  const t = useT();
28
+ const { sidebarHasNewRecordingAction } = usePageHeaderLayout();
27
29
  return (
28
30
  <LibraryGrid
29
31
  view="library"
30
32
  folderId={null}
31
33
  title="Library"
32
34
  extraActions={
33
- <Button
34
- className="gap-1.5 bg-primary text-primary-foreground shadow-sm hover:bg-primary/90"
35
- size="sm"
36
- asChild
37
- >
38
- <NavLink to="/record" aria-label={t("navigation.newRecording")}>
39
- <IconPlayerRecord className="h-4 w-4" />
40
- <span className="hidden sm:inline">
41
- {t("navigation.newRecording")}
42
- </span>
43
- </NavLink>
44
- </Button>
35
+ !sidebarHasNewRecordingAction && (
36
+ <Button
37
+ className="gap-1.5 bg-primary text-primary-foreground shadow-sm hover:bg-primary/90"
38
+ size="sm"
39
+ asChild
40
+ >
41
+ <NavLink to="/record" aria-label={t("navigation.newRecording")}>
42
+ <IconPlayerRecord className="h-4 w-4" />
43
+ <span className="hidden sm:inline">
44
+ {t("navigation.newRecording")}
45
+ </span>
46
+ </NavLink>
47
+ </Button>
48
+ )
45
49
  }
46
50
  />
47
51
  );
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-10
4
+ ---
5
+
6
+ New recording appears once when the library sidebar is open.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-10
4
+ ---
5
+
6
+ Slack setup now more clearly separates the legacy unfurl token fallback from workspace connections and messaging automations.
@@ -212,7 +212,7 @@ registerRequiredSecret({
212
212
  key: "SLACK_BOT_TOKEN",
213
213
  label: "Slack Bot Token (legacy)",
214
214
  description:
215
- "Legacy single-workspace bot token fallback for Clips link unfurls. New installs should use the Slack OAuth connect flow.",
215
+ "Legacy single-workspace bot token fallback for Clips link unfurls. New installs should use the Clips Slack workspace connection; new messaging automations should connect Slack in Settings > Messaging.",
216
216
  docsUrl: "https://api.slack.com/apps",
217
217
  scope: "workspace",
218
218
  kind: "api-key",
@@ -50,7 +50,6 @@ import {
50
50
  } from "@/hooks/use-notion";
51
51
  import {
52
52
  canWriteLinkedLocalSource,
53
- readDocumentFromLinkedLocalSource,
54
53
  writeDocumentToLinkedLocalSource,
55
54
  } from "@/lib/local-content-source-files";
56
55
  import { cn } from "@/lib/utils";
@@ -538,71 +537,6 @@ function DocumentEditorBody({ documentId, document }: DocumentEditorBodyProps) {
538
537
  }
539
538
  }, [document, documentId]);
540
539
 
541
- useEffect(() => {
542
- if (!isInitializedRef.current || !isLinkedLocalSourceDocument) {
543
- return;
544
- }
545
-
546
- let cancelled = false;
547
- readDocumentFromLinkedLocalSource(document)
548
- .then((result) => {
549
- if (cancelled) return;
550
- if (!result.ok) {
551
- toast.error(t("editor.couldNotReadLocalSourceFile"), {
552
- description: result.error,
553
- });
554
- return;
555
- }
556
-
557
- const fileDocument = result.document;
558
- setLocalTitle(fileDocument.title);
559
- setLocalContent(fileDocument.content);
560
- setLocalContentUpdatedAt(result.updatedAt);
561
- lastSavedTitleRef.current = {
562
- title: fileDocument.title,
563
- updatedAt: result.updatedAt,
564
- };
565
- lastSavedContentRef.current = {
566
- content: fileDocument.content,
567
- updatedAt: result.updatedAt,
568
- };
569
- queryClient.setQueryData(
570
- ["action", "get-document", { id: documentId }],
571
- (old: Document | undefined) =>
572
- old && typeof old === "object" ? { ...old, ...fileDocument } : old,
573
- );
574
- queryClient.setQueryData(
575
- ["action", "list-documents", undefined],
576
- (old: any) => {
577
- const docs = old?.documents ?? (Array.isArray(old) ? old : null);
578
- if (!Array.isArray(docs)) return old;
579
- const nextDocs = docs.map((doc: Document) =>
580
- doc.id === documentId ? { ...doc, ...fileDocument } : doc,
581
- );
582
- return Array.isArray(old)
583
- ? nextDocs
584
- : { ...old, documents: nextDocs };
585
- },
586
- );
587
- })
588
- .catch((error) => {
589
- if (cancelled) return;
590
- toast.error(t("editor.couldNotReadLocalSourceFile"), {
591
- description:
592
- error instanceof Error ? error.message : t("empty.genericError"),
593
- });
594
- });
595
-
596
- return () => {
597
- cancelled = true;
598
- };
599
- }, [
600
- documentId,
601
- document.source?.path,
602
- isLinkedLocalSourceDocument,
603
- queryClient,
604
- ]);
605
-
606
540
  // NOTE: External body changes (agent edit, Notion pull, update-document) are
607
541
  // reconciled into the editor by VisualEditor via its content prop + the
608
542
  // updatedAt gate. The effects below keep DocumentEditor's own mirror
@@ -814,13 +814,6 @@ export default function LocalFilesRoute() {
814
814
  count: restoredDirectories.length,
815
815
  }),
816
816
  });
817
- await pullDirectories(restoredDirectories, {
818
- showToast: false,
819
- syncControlResources: false,
820
- });
821
- await connectLocalComponentWorkspaces(restoredDirectories, {
822
- showToast: false,
823
- });
824
817
  return;
825
818
  }
826
819
 
@@ -835,13 +828,6 @@ export default function LocalFilesRoute() {
835
828
  count: restoredDirectories.length,
836
829
  }),
837
830
  });
838
- await pullDirectories(restoredDirectories, {
839
- showToast: false,
840
- syncControlResources: false,
841
- });
842
- await connectLocalComponentWorkspaces(restoredDirectories, {
843
- showToast: false,
844
- });
845
831
  };
846
832
  restoreDirectories()
847
833
  .catch((err) => {
@@ -943,32 +929,6 @@ export default function LocalFilesRoute() {
943
929
  return { directories: nextDirectories, result };
944
930
  }
945
931
 
946
- async function pullDirectories(
947
- selectedDirectories: SelectedDirectory[],
948
- {
949
- showToast = true,
950
- syncControlResources = true,
951
- }: { showToast?: boolean; syncControlResources?: boolean } = {},
952
- ) {
953
- let activeDirectories = selectedDirectories;
954
- let latestResult: ImportContentSourceResult | null = null;
955
- for (const directory of selectedDirectories) {
956
- const pulled = await pullDirectoryFiles(directory, activeDirectories, {
957
- syncControlResources,
958
- });
959
- latestResult = pulled.result;
960
- activeDirectories = pulled.directories;
961
- }
962
- if (latestResult) {
963
- setStatus({
964
- kind: "success",
965
- title: t("localFiles.foldersPulled"),
966
- detail: resultSummary(latestResult, t),
967
- });
968
- if (showToast) toast.success(t("localFiles.pulledLocalFiles"));
969
- }
970
- }
971
-
972
932
  async function connectLocalComponentWorkspaces(
973
933
  selectedDirectories: SelectedDirectory[],
974
934
  { showToast = true }: { showToast?: boolean } = {},
@@ -1029,7 +989,13 @@ export default function LocalFilesRoute() {
1029
989
  });
1030
990
 
1031
991
  setBusy(`pull:${selected.id}`);
1032
- await pullDirectories(nextDirectories);
992
+ const { result } = await pullDirectoryFiles(selected, nextDirectories);
993
+ setStatus({
994
+ kind: "success",
995
+ title: t("localFiles.foldersPulled"),
996
+ detail: resultSummary(result, t),
997
+ });
998
+ toast.success(t("localFiles.pulledLocalFiles"));
1033
999
  await connectLocalComponentWorkspaces([selected]);
1034
1000
  } catch (err) {
1035
1001
  setStatus({
@@ -1171,12 +1137,6 @@ export default function LocalFilesRoute() {
1171
1137
  title: t("localFiles.folderRemoved"),
1172
1138
  detail: directory.name,
1173
1139
  });
1174
- if (nextDirectories.length === 1) {
1175
- await pullDirectories(nextDirectories, {
1176
- showToast: false,
1177
- syncControlResources: false,
1178
- });
1179
- }
1180
1140
  } catch (err) {
1181
1141
  setStatus({
1182
1142
  kind: "error",
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-10
4
+ ---
5
+
6
+ Local folders now wait for an explicit pull before Content reads them
@@ -81,7 +81,7 @@
81
81
  "@swc/core": "^1.13.3",
82
82
  "@tailwindcss/typography": "^0.5.16",
83
83
  "@tailwindcss/vite": "catalog:",
84
- "@tanstack/react-query": "^5.99.2",
84
+ "@tanstack/react-query": "5.101.2",
85
85
  "@types/node": "^24.2.1",
86
86
  "@types/react": "catalog:",
87
87
  "@types/react-dom": "catalog:",