@agent-native/core 0.137.3 → 0.137.4

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 (178) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  3. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +53 -1
  4. package/corpus/templates/analytics/actions/gong-calls.ts +37 -17
  5. package/corpus/templates/analytics/app/lib/data-sources.ts +1 -1
  6. package/corpus/templates/analytics/server/lib/gong.ts +51 -2
  7. package/corpus/templates/assets/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  8. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +53 -1
  9. package/corpus/templates/brain/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  10. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +53 -1
  11. package/corpus/templates/brain/app/routes/settings.tsx +1 -1
  12. package/corpus/templates/brain/server/register-secrets.ts +1 -1
  13. package/corpus/templates/calendar/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  14. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +53 -1
  15. package/corpus/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  16. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +53 -1
  17. package/corpus/templates/chat/AGENTS.md +11 -0
  18. package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -1
  19. package/corpus/templates/chat/app/root.tsx +1 -1
  20. package/corpus/templates/chat/app/routes/agent.tsx +29 -13
  21. package/corpus/templates/chat/vite.config.ts +42 -0
  22. package/corpus/templates/clips/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  23. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +53 -1
  24. package/corpus/templates/clips/app/components/library/empty-state.tsx +1 -3
  25. package/corpus/templates/clips/app/components/library/library-grid.tsx +2 -6
  26. package/corpus/templates/clips/app/components/library/library-layout.tsx +2 -6
  27. package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +1 -3
  28. package/corpus/templates/clips/app/routes/bug-report.tsx +1 -8
  29. package/corpus/templates/clips/app/routes/import.tsx +1 -3
  30. package/corpus/templates/clips/changelog/2026-08-04-recording-buttons-are-now-text-only-for-a-cleaner-clips-inte.md +6 -0
  31. package/corpus/templates/clips/changelog/2026-08-04-save-settings-now-matches-the-other-clips-settings-buttons.md +6 -0
  32. package/corpus/templates/clips/chrome-extension/src/styles.css +1 -0
  33. package/corpus/templates/content/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  34. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +53 -1
  35. package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  36. package/corpus/templates/crm/app/routes/settings.tsx +2 -2
  37. package/corpus/templates/design/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  38. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +53 -1
  39. package/corpus/templates/design/actions/import-figma-clipboard.ts +1 -1
  40. package/corpus/templates/dispatch/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  41. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +53 -1
  42. package/corpus/templates/dispatch/changelog/2026-08-04-dispatch-keeps-connected-chats-out-of-local-history-by-defau.md +6 -0
  43. package/corpus/templates/factory/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  44. package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +53 -1
  45. package/corpus/templates/factory/README.md +1 -1
  46. package/corpus/templates/forms/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  47. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +53 -1
  48. package/corpus/templates/macros/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  49. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +53 -1
  50. package/corpus/templates/mail/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  51. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +53 -1
  52. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +0 -2
  53. package/corpus/templates/mail/app/pages/SettingsPage.tsx +1 -1
  54. package/corpus/templates/plan/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  55. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +53 -1
  56. package/corpus/templates/slides/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  57. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +53 -1
  58. package/corpus/templates/slides/actions/generate-image-api.ts +15 -4
  59. package/corpus/templates/slides/actions/import-google-slides-reference.ts +83 -0
  60. package/corpus/templates/slides/actions/import-pptx.ts +134 -117
  61. package/corpus/templates/slides/app/components/editor/GoogleSlidesReferenceImport.tsx +387 -0
  62. package/corpus/templates/slides/app/components/editor/ImageGenPanel.tsx +78 -48
  63. package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +377 -187
  64. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +0 -1
  65. package/corpus/templates/slides/app/hooks/use-deck-design-system.ts +20 -1
  66. package/corpus/templates/slides/app/i18n/en-US.ts +15 -0
  67. package/corpus/templates/slides/app/lib/new-deck-reference-selection.ts +22 -0
  68. package/corpus/templates/slides/app/pages/DeckEditor.tsx +4 -1
  69. package/corpus/templates/slides/app/pages/Index.tsx +172 -85
  70. package/corpus/templates/slides/server/lib/assets-image-delegation.ts +4 -2
  71. package/corpus/templates/slides/shared/api.ts +14 -1
  72. package/corpus/templates/tasks/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  73. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +53 -1
  74. package/dist/chat-threads/schema.d.ts +57 -0
  75. package/dist/chat-threads/schema.js +3 -0
  76. package/dist/chat-threads/store.d.ts +26 -0
  77. package/dist/chat-threads/store.js +84 -6
  78. package/dist/client/AgentPanel.js +11 -5
  79. package/dist/client/ConnectBuilderCard.js +1 -1
  80. package/dist/client/agent-page/AgentEmptyState.d.ts +2 -1
  81. package/dist/client/agent-page/AgentEmptyState.js +6 -2
  82. package/dist/client/agent-page/AgentJobsTab.d.ts +3 -1
  83. package/dist/client/agent-page/AgentJobsTab.js +55 -44
  84. package/dist/client/agent-page/AgentTabFrame.d.ts +2 -1
  85. package/dist/client/agent-page/AgentTabFrame.js +4 -2
  86. package/dist/client/agent-page/AgentTabsPage.js +12 -8
  87. package/dist/client/agent-page/AgentWorkspaceContent.d.ts +10 -0
  88. package/dist/client/agent-page/AgentWorkspaceContent.js +110 -0
  89. package/dist/client/agent-page/AutomationDetailsDialog.js +1 -1
  90. package/dist/client/chat/run-recovery.js +3 -3
  91. package/dist/client/error-format.js +1 -1
  92. package/dist/client/integrations/IntegrationsPanel.d.ts +1 -0
  93. package/dist/client/integrations/IntegrationsPanel.js +149 -5
  94. package/dist/client/onboarding/FirstRunOnboarding.js +130 -15
  95. package/dist/client/onboarding/index.d.ts +1 -1
  96. package/dist/client/onboarding/index.js +1 -1
  97. package/dist/client/onboarding/use-onboarding.js +12 -2
  98. package/dist/client/onboarding/use-preview-mode.d.ts +4 -0
  99. package/dist/client/onboarding/use-preview-mode.js +9 -0
  100. package/dist/client/resources/BuiltinCapabilityDetail.js +2 -2
  101. package/dist/client/resources/ResourceTree.d.ts +4 -1
  102. package/dist/client/resources/ResourceTree.js +13 -8
  103. package/dist/client/resources/ResourcesPanel.js +7 -3
  104. package/dist/client/resources/mcp-integration-catalog.js +4 -4
  105. package/dist/client/resources/use-mcp-servers.js +6 -6
  106. package/dist/client/settings/AgentsSection.js +15 -16
  107. package/dist/client/settings/AutomationsSection.js +1 -1
  108. package/dist/client/settings/DemoModeSection.js +1 -1
  109. package/dist/client/settings/SettingsPanel.d.ts +3 -1
  110. package/dist/client/settings/SettingsPanel.js +284 -88
  111. package/dist/client/settings/SettingsRow.js +1 -1
  112. package/dist/client/settings/SettingsSection.d.ts +6 -4
  113. package/dist/client/settings/SettingsSection.js +6 -6
  114. package/dist/client/settings/SettingsTabsPage.js +47 -25
  115. package/dist/client/settings/VoiceTranscriptionSection.d.ts +3 -1
  116. package/dist/client/settings/VoiceTranscriptionSection.js +20 -8
  117. package/dist/client/settings/agent-settings-search.d.ts +1 -1
  118. package/dist/client/settings/agent-settings-search.js +8 -8
  119. package/dist/client/transcription/BuilderTranscriptionCta.js +2 -2
  120. package/dist/client/use-chat-threads.d.ts +9 -0
  121. package/dist/client/use-chat-threads.js +22 -6
  122. package/dist/collab/awareness.d.ts +2 -2
  123. package/dist/collab/routes.d.ts +1 -1
  124. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  125. package/dist/integrations/google-docs-poller.js +9 -1
  126. package/dist/integrations/webhook-handler.js +11 -1
  127. package/dist/jobs/scheduler.d.ts +4 -1
  128. package/dist/jobs/scheduler.js +34 -9
  129. package/dist/localization/default-messages.js +40 -40
  130. package/dist/mcp-client/oauth-routes.js +1 -1
  131. package/dist/notifications/routes.d.ts +1 -1
  132. package/dist/provider-api/actions/custom-provider-registration.d.ts +14 -14
  133. package/dist/provider-api/actions/provider-api.d.ts +15 -15
  134. package/dist/secrets/onboarding.js +1 -1
  135. package/dist/secrets/routes.d.ts +6 -6
  136. package/dist/server/agent-chat/browser-team-tools.d.ts +1 -0
  137. package/dist/server/agent-chat/browser-team-tools.js +1 -0
  138. package/dist/server/agent-chat-plugin.js +14 -1
  139. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  140. package/dist/server/agent-teams.d.ts +2 -0
  141. package/dist/server/agent-teams.js +2 -0
  142. package/dist/server/realtime-token.d.ts +1 -1
  143. package/dist/server/workspace-provider-oauth.js +1 -1
  144. package/dist/setup-connections/onboarding.js +2 -2
  145. package/dist/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  146. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +53 -1
  147. package/dist/templates/chat/AGENTS.md +11 -0
  148. package/dist/templates/chat/app/components/layout/Layout.tsx +1 -1
  149. package/dist/templates/chat/app/root.tsx +1 -1
  150. package/dist/templates/chat/app/routes/agent.tsx +29 -13
  151. package/dist/templates/chat/vite.config.ts +42 -0
  152. package/dist/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  153. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +53 -1
  154. package/dist/templates/default/AGENTS.md +11 -0
  155. package/dist/templates/default/vite.config.ts +42 -0
  156. package/dist/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  157. package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  158. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +53 -1
  159. package/dist/templates/workspace-core/AGENTS.md +11 -0
  160. package/dist/templates/workspace-root/AGENTS.md +14 -0
  161. package/dist/vite/client.js +56 -0
  162. package/package.json +2 -2
  163. package/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  164. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +53 -1
  165. package/src/templates/chat/AGENTS.md +11 -0
  166. package/src/templates/chat/app/components/layout/Layout.tsx +1 -1
  167. package/src/templates/chat/app/root.tsx +1 -1
  168. package/src/templates/chat/app/routes/agent.tsx +29 -13
  169. package/src/templates/chat/vite.config.ts +42 -0
  170. package/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  171. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +53 -1
  172. package/src/templates/default/AGENTS.md +11 -0
  173. package/src/templates/default/vite.config.ts +42 -0
  174. package/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  175. package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +33 -0
  176. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +53 -1
  177. package/src/templates/workspace-core/AGENTS.md +11 -0
  178. package/src/templates/workspace-root/AGENTS.md +14 -0
@@ -6,7 +6,7 @@ import { cn } from "../utils.js";
6
6
  * resolve an app's i18n keys.
7
7
  */
8
8
  export function SettingsGroup({ id, title, description, className, children, }) {
9
- return (_jsxs("section", { id: id, className: cn("scroll-mt-16 overflow-hidden rounded-lg border border-border bg-card text-card-foreground shadow-sm", className), children: [(title || description) && (_jsxs("header", { className: "border-b border-border/60 px-5 py-4 sm:px-6", children: [title && (_jsx("h2", { className: "text-base font-semibold leading-none tracking-tight", children: title })), description && (_jsx("p", { className: "mt-1.5 text-sm leading-snug text-muted-foreground", children: description }))] })), _jsx("div", { className: "divide-y divide-border/60", children: children })] }));
9
+ return (_jsxs("section", { id: id, className: cn("scroll-mt-16", className), children: [(title || description) && (_jsxs("header", { className: "mb-2.5", children: [title && (_jsx("h2", { className: "text-sm font-semibold text-foreground", children: title })), description && (_jsx("p", { className: "mt-1 text-xs leading-5 text-muted-foreground", children: description }))] })), _jsx("div", { className: "overflow-hidden rounded-xl border border-border/70 bg-card text-card-foreground", children: _jsx("div", { className: "divide-y divide-border/60", children: children }) })] }));
10
10
  }
11
11
  /** One setting: label and description on the start side, control on the end. */
12
12
  export function SettingsRow({ id, label, description, icon, status, control, children, className, }) {
@@ -20,15 +20,17 @@ interface SettingsSectionProps {
20
20
  badge?: string;
21
21
  required?: boolean;
22
22
  connected?: boolean;
23
+ /** Let a child-owned card surface fill the section without a second wrapper. */
24
+ flat?: boolean;
25
+ /** Render the page content as a row inside a parent SettingsGroup. */
26
+ grouped?: boolean;
23
27
  open?: boolean;
24
28
  onToggle?: () => void;
25
29
  children: ReactNode;
26
30
  }
27
31
  /**
28
- * Collapsible settings section. Renders as a compact row in the agent sidebar
29
- * and as a polished, shadcn-style card on the full settings page (so the
30
- * framework tabs match app-owned General/Team cards). The visual surface is
31
- * read from `SettingsSurfaceContext`.
32
+ * Settings sections stay compact and collapsible in the agent sidebar, while
33
+ * the full settings page uses Codex-style headings and always-visible rows.
32
34
  */
33
35
  export declare function SettingsSection(props: SettingsSectionProps): import("react").JSX.Element;
34
36
  export {};
@@ -10,10 +10,8 @@ export function useSettingsSurface() {
10
10
  return useContext(SettingsSurfaceContext);
11
11
  }
12
12
  /**
13
- * Collapsible settings section. Renders as a compact row in the agent sidebar
14
- * and as a polished, shadcn-style card on the full settings page (so the
15
- * framework tabs match app-owned General/Team cards). The visual surface is
16
- * read from `SettingsSurfaceContext`.
13
+ * Settings sections stay compact and collapsible in the agent sidebar, while
14
+ * the full settings page uses Codex-style headings and always-visible rows.
17
15
  */
18
16
  export function SettingsSection(props) {
19
17
  const surface = useSettingsSurface();
@@ -59,8 +57,10 @@ function SettingsSectionBody({ open, children, }) {
59
57
  return null;
60
58
  return (_jsx("div", { "data-state": open ? "open" : "closed", "aria-hidden": open ? undefined : true, inert: open ? undefined : true, className: "agent-native-settings-section-body", onTransitionEnd: handleTransitionEnd, children: children }));
61
59
  }
62
- function PageSettingsSection({ id, icon, title, subtitle, badge, required, connected, open = false, onToggle, children, }) {
63
- return (_jsxs("div", { id: id, className: cn("scroll-mt-16 overflow-hidden rounded-lg border bg-card text-card-foreground shadow-sm transition-colors", open ? "border-border" : "border-border/60"), children: [_jsxs("button", { type: "button", onClick: onToggle, "aria-expanded": open, className: "flex w-full items-start justify-between gap-4 px-5 py-4 text-start transition-colors hover:bg-muted/40 sm:px-6 sm:py-5", children: [_jsxs("div", { className: "flex min-w-0 items-start gap-3", children: [_jsx("span", { className: "mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground [&>svg]:size-[18px]", children: icon }), _jsxs("span", { className: "flex min-w-0 flex-col gap-1.5", children: [_jsxs("span", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "text-base font-semibold leading-none tracking-tight text-foreground", children: title }), connected && _jsx(ConnectedDot, { size: "md" }), required && !connected && (_jsx(StatusBadge, { label: "Required", tone: "required", size: "md" })), badge && _jsx(StatusBadge, { label: badge, tone: "muted", size: "md" })] }), subtitle && (_jsx("span", { className: "text-sm leading-snug text-muted-foreground", children: subtitle }))] })] }), _jsx(IconChevronDown, { size: 18, className: cn("mt-1 shrink-0 text-muted-foreground transition-transform", open && "rotate-180") })] }), _jsx(SettingsSectionBody, { open: open, children: _jsx("div", { "data-agent-native-settings-page": "", className: "border-t border-border/60 px-5 pb-5 pt-5 sm:px-6 sm:pb-6", children: children }) })] }));
60
+ function PageSettingsSection({ id, title, subtitle, badge, required, connected, flat, grouped, children, }) {
61
+ return (_jsxs("section", { id: id, className: "scroll-mt-16", children: [!grouped && (_jsx("div", { className: "mb-2.5 flex items-baseline justify-between gap-3", children: _jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("h2", { className: "text-sm font-semibold text-foreground", children: title }), connected && _jsx(ConnectedDot, { size: "sm" }), required && !connected && (_jsx(StatusBadge, { label: "Required", tone: "required", size: "sm" })), badge && _jsx(StatusBadge, { label: badge, tone: "muted", size: "sm" })] }), subtitle && (_jsx("p", { className: "mt-1 text-xs leading-5 text-muted-foreground", children: subtitle }))] }) })), _jsx("div", { "data-agent-native-settings-page": "", className: cn(!flat &&
62
+ !grouped &&
63
+ "overflow-hidden rounded-xl border border-border/70 bg-card text-card-foreground"), children: _jsx("div", { className: cn(!flat && !grouped && "divide-y divide-border/60 px-5 py-1 sm:px-6"), children: children }) })] }));
64
64
  }
65
65
  function SidebarSettingsSection({ id, icon, title, subtitle, badge, required, connected, open = false, onToggle, children, }) {
66
66
  return (_jsxs("div", { id: id, className: "rounded-lg border border-border bg-background/50", children: [_jsxs("button", { type: "button", onClick: onToggle, "aria-expanded": open, className: "flex w-full cursor-pointer items-center justify-between px-3 py-2.5 text-start rounded-lg hover:bg-accent/40 transition-colors", children: [_jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [_jsx("span", { className: "shrink-0 text-muted-foreground", children: icon }), _jsx("span", { className: "text-[12px] font-medium text-foreground truncate", children: title }), connected && _jsx(ConnectedDot, { size: "sm" }), required && !connected && (_jsx(StatusBadge, { label: "Required", tone: "muted", size: "sm" })), badge && _jsx(StatusBadge, { label: badge, tone: "muted", size: "sm" })] }), _jsx(IconChevronDown, { size: 12, className: `shrink-0 text-muted-foreground transition-transform ${open ? "rotate-180" : ""}` })] }), _jsx(SettingsSectionBody, { open: open, children: _jsxs("div", { className: "border-t border-border px-3 pb-3 pt-2.5", children: [subtitle && (_jsx("p", { className: "text-[10px] text-muted-foreground mb-2.5", children: subtitle })), children] }) })] }));
@@ -1,13 +1,19 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Tabs, useDesignSystem } from "@agent-native/toolkit/design-system";
3
- import { IconArrowUpRight, IconHistory, IconSearch, IconSettings, IconUserCircle, IconUsers, IconX, } from "@tabler/icons-react";
3
+ import { IconArrowLeft, IconArrowUpRight, IconHistory, IconSearch, IconSettings, IconUserCircle, IconUsers, IconX, } from "@tabler/icons-react";
4
4
  import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
5
5
  import { Link } from "react-router";
6
6
  import { cn } from "../utils.js";
7
7
  function normalizeTabId(value) {
8
- const normalized = value
9
- ?.replace(/^#/, "")
10
- .trim()
8
+ let decoded = value?.replace(/^#/, "").trim() ?? "";
9
+ try {
10
+ decoded = decodeURIComponent(decoded);
11
+ }
12
+ catch {
13
+ // coercion-ok: preserve malformed external hashes for routing fallback.
14
+ // Keep the raw hash when an external link contains malformed encoding.
15
+ }
16
+ const normalized = decoded
11
17
  .toLowerCase()
12
18
  .replace(/['"]/g, "")
13
19
  .replace(/[\s_]+/g, "-");
@@ -22,6 +28,9 @@ function normalizeTabId(value) {
22
28
  if (normalized === "workspace" || normalized === "workspace-settings") {
23
29
  return "workspace";
24
30
  }
31
+ if (normalized === "connections") {
32
+ return "integrations";
33
+ }
25
34
  return normalized;
26
35
  }
27
36
  function resolveTabId(tabs, value) {
@@ -30,7 +39,15 @@ function resolveTabId(tabs, value) {
30
39
  return null;
31
40
  if (tabs.some((tab) => tab.id === normalized))
32
41
  return normalized;
42
+ const nestedTab = tabs
43
+ .filter((tab) => normalized.startsWith(`${tab.id}:`) ||
44
+ tab.id.startsWith(`${normalized}:`))
45
+ .sort((a, b) => b.id.length - a.id.length)[0];
46
+ if (nestedTab)
47
+ return nestedTab.id;
33
48
  const section = normalized.split(":", 1)[0];
49
+ if (tabs.some((tab) => tab.id === section))
50
+ return section;
34
51
  const owner = tabs.find((tab) => tab.searchEntries?.some((entry) => normalizeTabId(entry.hash ?? entry.id) === section));
35
52
  if (owner)
36
53
  return owner.id;
@@ -53,7 +70,7 @@ function updateHashForTab(tabId) {
53
70
  if (typeof window === "undefined")
54
71
  return;
55
72
  const { pathname, search } = window.location;
56
- const hash = tabId === "general" ? "" : `#${encodeURIComponent(tabId)}`;
73
+ const hash = tabId === "general" ? "" : `#${tabId}`;
57
74
  window.history.pushState(null, "", `${pathname}${search}${hash}`);
58
75
  }
59
76
  function isEditableElement(element) {
@@ -65,7 +82,7 @@ function isEditableElement(element) {
65
82
  tagName === "select" ||
66
83
  element.isContentEditable);
67
84
  }
68
- export function SettingsTabsPage({ general, account, team, whatsNew, extraTabs = [], generalLabel = "General", accountLabel = "Account", teamLabel = "Team", whatsNewLabel = "What's new", ariaLabel = "Settings sections", defaultTab = "general", className, navClassName, contentClassName, enableSearch = true, searchPlaceholder = "Search settings", searchEntries, generalSearchEntries, value, onValueChange, }) {
85
+ export function SettingsTabsPage({ general, account, team, whatsNew, extraTabs = [], generalLabel = "General", accountLabel = "Account", teamLabel = "Team", whatsNewLabel = "What's new", ariaLabel = "Settings sections", defaultTab = "integrations", className, navClassName, contentClassName, enableSearch = true, searchPlaceholder = "Search settings", searchEntries, generalSearchEntries, value, onValueChange, }) {
69
86
  const rootRef = useRef(null);
70
87
  const searchInputRef = useRef(null);
71
88
  const autoFocusedSearchRef = useRef(false);
@@ -142,6 +159,12 @@ export function SettingsTabsPage({ general, account, team, whatsNew, extraTabs =
142
159
  }
143
160
  return groups;
144
161
  }, [tabs]);
162
+ const tabGroupLabels = {
163
+ app: "Personal",
164
+ integrations: "Integrations",
165
+ workspace: "Workspace",
166
+ agent: "Agent",
167
+ };
145
168
  const isControlled = value !== undefined;
146
169
  const [internalTab, setInternalTab] = useState(() => activeTabFromHash(tabs, fallbackTab));
147
170
  const activeTab = isControlled ? value : internalTab;
@@ -300,7 +323,7 @@ export function SettingsTabsPage({ general, account, team, whatsNew, extraTabs =
300
323
  }
301
324
  };
302
325
  const searching = enableSearch && trimmedQuery.length > 0;
303
- return (_jsxs("div", { ref: rootRef, className: cn("flex h-full min-h-0 w-full flex-col overflow-hidden bg-background sm:flex-row", className), children: [_jsxs("div", { className: cn("flex shrink-0 flex-col gap-2 bg-background p-2 sm:min-h-0 sm:w-56 sm:overflow-y-auto sm:p-3", navClassName), children: [enableSearch ? (_jsxs("div", { className: "relative sm:mb-1", children: [_jsx(IconSearch, { className: "pointer-events-none absolute start-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" }), _jsx("input", { ref: searchInputRef, type: "search", value: query, onChange: (event) => setQuery(event.target.value), onKeyDown: (event) => {
326
+ return (_jsxs("div", { ref: rootRef, className: cn("flex h-full min-h-0 w-full flex-col overflow-hidden bg-background sm:flex-row", className), children: [_jsxs("div", { className: cn("flex shrink-0 flex-col gap-2 bg-background p-2 sm:min-h-0 sm:w-60 sm:overflow-y-auto sm:p-4", navClassName), children: [_jsxs("a", { href: "/", className: "flex min-h-8 items-center gap-2 rounded-md px-2 text-sm text-muted-foreground transition-colors hover:bg-accent/50 hover:text-foreground", children: [_jsx(IconArrowLeft, { className: "size-4 shrink-0 rtl:-scale-x-100" }), _jsx("span", { children: "Back to app" })] }), enableSearch ? (_jsxs("div", { className: "relative sm:mb-2", children: [_jsx(IconSearch, { className: "pointer-events-none absolute start-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" }), _jsx("input", { ref: searchInputRef, type: "search", value: query, onChange: (event) => setQuery(event.target.value), onKeyDown: (event) => {
304
327
  if (event.key === "Escape")
305
328
  setQuery("");
306
329
  if (event.key === "Enter" && results[0]) {
@@ -329,25 +352,24 @@ export function SettingsTabsPage({ general, account, team, whatsNew, extraTabs =
329
352
  changeTab(nextTab);
330
353
  if (!isControlled)
331
354
  updateHashForTab(nextTab);
332
- }, orientation: "vertical", "aria-label": ariaLabel, className: "flex gap-1 overflow-x-auto sm:flex-col sm:overflow-x-visible" })) : (_jsx("nav", { "aria-label": ariaLabel, role: "tablist", className: "flex gap-1 overflow-x-auto sm:flex-col sm:overflow-x-visible", children: tabGroups.map((group, groupIndex) => (_jsx("div", { "data-settings-tab-group": group.id, className: cn("contents sm:block", groupIndex > 0 &&
333
- "sm:mt-2 sm:border-t sm:border-border/60 sm:pt-2"), children: _jsx("div", { className: "contents sm:flex sm:flex-col sm:gap-1", children: group.tabs.map((tab) => {
334
- const Icon = tab.icon;
335
- const selected = tab.id === selectedTab?.id;
336
- const tabContent = (_jsxs(_Fragment, { children: [Icon ? (_jsx(Icon, { className: cn("size-4 shrink-0", selected
337
- ? "text-foreground"
338
- : "text-muted-foreground") })) : null, _jsx("span", { className: "truncate", children: tab.label }), tab.href ? (_jsx(IconArrowUpRight, { "aria-hidden": "true", className: "size-3.5 shrink-0 text-muted-foreground/80" })) : null] }));
339
- if (tab.href) {
340
- return (_jsx(Link, { role: "tab", "aria-selected": selected, to: tab.href, className: cn("flex min-h-9 shrink-0 items-center gap-2 rounded-md px-3 py-2 text-start text-sm font-medium transition-colors sm:w-full", selected
355
+ }, orientation: "vertical", "aria-label": ariaLabel, className: "flex gap-1 overflow-x-auto sm:flex-col sm:overflow-x-visible" })) : (_jsx("nav", { "aria-label": ariaLabel, role: "tablist", className: "flex gap-1 overflow-x-auto sm:flex-col sm:overflow-x-visible", children: tabGroups.map((group, groupIndex) => (_jsx("div", { "data-settings-tab-group": group.id, className: cn("contents sm:block", groupIndex > 0 && "sm:mt-3 sm:pt-1"), children: _jsxs("div", { className: "contents sm:flex sm:flex-col sm:gap-1", children: [_jsx("div", { className: "hidden px-3 pb-1 pt-1 text-[11px] font-medium text-muted-foreground sm:block", children: tabGroupLabels[group.id] ?? group.id }), group.tabs.map((tab) => {
356
+ const Icon = tab.icon;
357
+ const selected = tab.id === selectedTab?.id;
358
+ const tabContent = (_jsxs(_Fragment, { children: [Icon ? (_jsx(Icon, { className: cn("size-4 shrink-0", selected
359
+ ? "text-foreground"
360
+ : "text-muted-foreground") })) : null, _jsx("span", { className: "truncate", children: tab.label }), tab.href ? (_jsx(IconArrowUpRight, { "aria-hidden": "true", className: "size-3.5 shrink-0 text-muted-foreground/80" })) : null] }));
361
+ if (tab.href) {
362
+ return (_jsx(Link, { role: "tab", "aria-selected": selected, to: tab.href, className: cn("flex min-h-9 shrink-0 items-center gap-2 rounded-md px-3 py-2 text-start text-sm font-medium transition-colors sm:w-full", selected
363
+ ? "bg-accent text-foreground"
364
+ : "text-muted-foreground hover:bg-accent/50 hover:text-foreground"), children: tabContent }, tab.id));
365
+ }
366
+ return (_jsx("button", { type: "button", role: "tab", "aria-selected": selected, "aria-controls": `settings-tabpanel-${tab.id}`, id: `settings-tab-${tab.id}`, onClick: () => {
367
+ changeTab(tab.id);
368
+ if (!isControlled)
369
+ updateHashForTab(tab.id);
370
+ }, className: cn("flex min-h-9 shrink-0 items-center gap-2 rounded-md px-3 py-2 text-start text-sm font-medium transition-colors sm:w-full", selected
341
371
  ? "bg-accent text-foreground"
342
372
  : "text-muted-foreground hover:bg-accent/50 hover:text-foreground"), children: tabContent }, tab.id));
343
- }
344
- return (_jsx("button", { type: "button", role: "tab", "aria-selected": selected, "aria-controls": `settings-tabpanel-${tab.id}`, id: `settings-tab-${tab.id}`, onClick: () => {
345
- changeTab(tab.id);
346
- if (!isControlled)
347
- updateHashForTab(tab.id);
348
- }, className: cn("flex min-h-9 shrink-0 items-center gap-2 rounded-md px-3 py-2 text-start text-sm font-medium transition-colors sm:w-full", selected
349
- ? "bg-accent text-foreground"
350
- : "text-muted-foreground hover:bg-accent/50 hover:text-foreground"), children: tabContent }, tab.id));
351
- }) }) }, group.id))) }))] }), _jsx("div", { id: `settings-tabpanel-${selectedTab?.id ?? "general"}`, role: "tabpanel", "aria-labelledby": `settings-tab-${selectedTab?.id ?? "general"}`, className: cn("min-h-0 min-w-0 flex-1 overflow-y-auto p-4 sm:p-6", contentClassName), children: selectedTab?.content })] }));
373
+ })] }) }, group.id))) }))] }), _jsx("div", { id: `settings-tabpanel-${selectedTab?.id ?? "general"}`, role: "tabpanel", "aria-labelledby": `settings-tab-${selectedTab?.id ?? "general"}`, className: cn("min-h-0 min-w-0 flex-1 overflow-y-auto p-4 sm:p-8 lg:p-10", contentClassName), children: selectedTab?.content })] }));
352
374
  }
353
375
  //# sourceMappingURL=SettingsTabsPage.js.map
@@ -10,5 +10,7 @@
10
10
  * mirrors the server transcription route's key/env resolution.
11
11
  */
12
12
  import React from "react";
13
- export declare function VoiceTranscriptionSection(): React.JSX.Element;
13
+ export declare function VoiceTranscriptionSection({ compact, }?: {
14
+ compact?: boolean;
15
+ }): React.JSX.Element;
14
16
  //# sourceMappingURL=VoiceTranscriptionSection.d.ts.map
@@ -10,10 +10,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
10
  * Provider status comes from `/_agent-native/voice-providers/status`, which
11
11
  * mirrors the server transcription route's key/env resolution.
12
12
  */
13
- import { Switch } from "@agent-native/toolkit/design-system";
13
+ import { Picker, Switch } from "@agent-native/toolkit/design-system";
14
14
  import { IconAlertCircle, IconCheck, IconChevronDown, IconChevronRight, IconExternalLink, IconLoader2, IconLockOpen, } from "@tabler/icons-react";
15
15
  import { useCallback, useEffect, useState } from "react";
16
16
  import { agentNativePath } from "../api-path.js";
17
+ import { SettingsRow } from "./SettingsRow.js";
17
18
  import { openBuilderConnectPopup, useBuilderStatus, } from "./useBuilderStatus.js";
18
19
  const PREFS_URL = agentNativePath("/_agent-native/application-state/voice-transcription-prefs");
19
20
  const CLEANUP_PREFS_URL = agentNativePath("/_agent-native/application-state/voice-cleanup-prefs");
@@ -58,7 +59,7 @@ function batchProvider(provider) {
58
59
  return DEFAULT_BATCH_PROVIDER;
59
60
  return provider;
60
61
  }
61
- export function VoiceTranscriptionSection() {
62
+ export function VoiceTranscriptionSection({ compact = false, } = {}) {
62
63
  const [transcriptionMode, setTranscriptionMode] = useState(null);
63
64
  const [provider, setProvider] = useState(DEFAULT_BATCH_PROVIDER);
64
65
  const [instructions, setInstructions] = useState("");
@@ -267,14 +268,25 @@ export function VoiceTranscriptionSection() {
267
268
  if (transcriptionMode === null) {
268
269
  return (_jsxs("div", { className: "flex items-center gap-1.5 text-[10px] text-muted-foreground", children: [_jsx(IconLoader2, { size: 10, className: "animate-spin" }), "Loading\u2026"] }));
269
270
  }
271
+ if (compact) {
272
+ return (_jsx(SettingsRow, { label: "Voice transcription", description: "Choose how voice input is transcribed.", control: _jsx(Picker, { mode: "select", options: [
273
+ { value: "mac-native", label: "Mac Native" },
274
+ { value: "google-realtime", label: "Google Realtime" },
275
+ { value: "batch", label: "Batch" },
276
+ ], value: transcriptionMode, onChange: (next) => {
277
+ const value = String(next ?? "");
278
+ if (isTranscriptionMode(value))
279
+ chooseSource(value);
280
+ }, "aria-label": "Voice transcription", className: "w-44 text-start" }) }));
281
+ }
270
282
  return (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "rounded-md border border-border bg-background p-2", children: [_jsx("div", { className: "mb-2 flex items-start justify-between gap-3 px-0.5", children: _jsxs("div", { children: [_jsx("div", { className: "text-[11px] font-medium text-foreground", children: "Live transcription" }), _jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "Choose where real-time words come from. Batch still runs after recording stops." })] }) }), _jsxs("div", { className: "space-y-2", children: [_jsx(ProviderOption, { id: "mac-native", selected: transcriptionMode === "mac-native", onSelect: () => chooseSource("mac-native"), title: "Mac Native", subtitle: "Free and fast in the macOS Tauri app. Web clients use the existing browser-native path when available.", rightSlot: _jsx("span", { className: "text-[10px] text-muted-foreground", children: "Tauri default" }) }), _jsx(ProviderOption, { id: "google-realtime", selected: transcriptionMode === "google-realtime", onSelect: () => chooseSource("google-realtime"), disabled: !googleRealtimeReady, title: "Google Realtime", subtitle: googleRealtimeReady
271
283
  ? "BYOK only for v1. Streams live partials and finals through Google Speech-to-Text."
272
284
  : googleRealtimeConfigured
273
285
  ? "Google credentials are set. Connect Builder completely to mint the managed realtime session."
274
- : "BYOK only for v1. Configure Google service account before selecting this source.", rightSlot: googleRealtimeReady ? (_jsxs("span", { className: "flex items-center gap-1 text-[10px] text-green-500", children: [_jsx(IconCheck, { size: 10 }), "Ready"] })) : googleRealtimeConfigured ? (_jsxs("button", { type: "button", onClick: (e) => {
286
+ : "BYOK only for v1. Configure Google service account before selecting this source.", rightSlot: googleRealtimeReady ? (_jsxs("span", { className: "flex items-center gap-1 text-[10px] text-green-500", children: [_jsx(IconCheck, { size: 10 }), "Ready"] })) : googleRealtimeConfigured ? (_jsx("button", { type: "button", onClick: (e) => {
275
287
  e.stopPropagation();
276
288
  openBuilderConnect();
277
- }, className: "inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:bg-accent/40 hover:text-foreground", children: ["Connect Builder.io", _jsx(IconExternalLink, { size: 10 })] })) : (_jsxs("button", { type: "button", onClick: (e) => {
289
+ }, className: "inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:bg-accent/40 hover:text-foreground", children: "Connect Builder.io" })) : (_jsxs("button", { type: "button", onClick: (e) => {
278
290
  e.stopPropagation();
279
291
  setShowAdvanced(true);
280
292
  focusKey("GOOGLE_APPLICATION_CREDENTIALS");
@@ -285,18 +297,18 @@ export function VoiceTranscriptionSection() {
285
297
  : "Needs key" }))] })] }), _jsxs("div", { className: "rounded-md border border-border bg-background", children: [_jsxs("button", { type: "button", onClick: () => setShowAdvanced((v) => !v), className: "w-full flex items-center justify-between gap-2 px-2.5 py-2 cursor-pointer", children: [_jsxs("span", { className: "text-[11px] font-medium text-foreground inline-flex items-center gap-1", children: [showAdvanced ? (_jsx(IconChevronDown, { size: 12 })) : (_jsx(IconChevronRight, { size: 12, className: "rtl:-scale-x-100" })), "Add API keys"] }), _jsx("span", { className: "text-[10px] text-muted-foreground", children: "Google STT \u00B7 Gemini \u00B7 Groq \u00B7 OpenAI" })] }), showAdvanced && (_jsxs("div", { className: "px-2 pb-2 space-y-2", children: [_jsx(ProviderOption, { id: "google-service-account", selected: transcriptionMode === "google-realtime", onSelect: () => chooseSource("google-realtime"), disabled: !googleRealtimeReady, title: "Google Speech-to-Text service account", subtitle: googleRealtimeConfigured
286
298
  ? "Service-account JSON is set. Connect Builder to mint the managed realtime WebSocket session."
287
299
  : "Service-account JSON for the dedicated realtime WebSocket to Google StreamingRecognize.", rightSlot: googleRealtimeConfigured ===
288
- null ? null : googleRealtimeReady ? (_jsxs("span", { className: "flex items-center gap-1 text-[10px] text-green-500", children: [_jsx(IconCheck, { size: 10 }), "Ready"] })) : googleRealtimeConfigured ? (_jsxs("button", { type: "button", onClick: (e) => {
300
+ null ? null : googleRealtimeReady ? (_jsxs("span", { className: "flex items-center gap-1 text-[10px] text-green-500", children: [_jsx(IconCheck, { size: 10 }), "Ready"] })) : googleRealtimeConfigured ? (_jsx("button", { type: "button", onClick: (e) => {
289
301
  e.stopPropagation();
290
302
  openBuilderConnect();
291
- }, className: "inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-accent/40", children: ["Connect Builder.io", _jsx(IconExternalLink, { size: 10 })] })) : (_jsxs("button", { type: "button", onClick: (e) => {
303
+ }, className: "inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-accent/40", children: "Connect Builder.io" })) : (_jsxs("button", { type: "button", onClick: (e) => {
292
304
  e.stopPropagation();
293
305
  focusKey("GOOGLE_APPLICATION_CREDENTIALS");
294
306
  }, className: "inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-accent/40", children: ["Configure", _jsx(IconExternalLink, { size: 10 })] })) }), _jsx(ProviderOption, { id: "auto", selected: transcriptionMode === "batch" && provider === "auto", onSelect: () => chooseBatchProvider("auto"), title: "Automatic batch fallback", subtitle: "Keep the current Clips fallback chain: Builder Gemini, Gemini, Groq, then OpenAI." }), _jsx(ProviderOption, { id: "builder-gemini", selected: transcriptionMode === "batch" && provider === "builder-gemini", onSelect: () => chooseBatchProvider("builder-gemini"), disabled: !builderStatus?.configured, title: "Builder.io Connect", subtitle: builderStatus?.configured
295
307
  ? "Use Builder-hosted Gemini Flash-Lite for batch transcription and Luna for text cleanup."
296
- : "One-click connect for Gemini Flash-Lite transcription and Luna text cleanup. No Google key needed.", rightSlot: builderStatus?.configured ? (_jsxs("span", { className: "flex items-center gap-1 text-[10px] text-green-500", children: [_jsx(IconCheck, { size: 10 }), "Connected"] })) : (_jsxs("button", { type: "button", onClick: (e) => {
308
+ : "One-click connect for Gemini Flash-Lite transcription and Luna text cleanup. No Google key needed.", rightSlot: builderStatus?.configured ? (_jsxs("span", { className: "flex items-center gap-1 text-[10px] text-green-500", children: [_jsx(IconCheck, { size: 10 }), "Connected"] })) : (_jsx("button", { type: "button", onClick: (e) => {
297
309
  e.stopPropagation();
298
310
  openBuilderConnect();
299
- }, className: "inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-accent/40", children: ["Connect Builder.io", _jsx(IconExternalLink, { size: 10 })] })) }), _jsx(ProviderOption, { id: "gemini", selected: transcriptionMode === "batch" && provider === "gemini", onSelect: () => chooseBatchProvider("gemini"), title: "Google Gemini", subtitle: "BYOK Gemini for AI cleanup and optional strict batch transcription.", rightSlot: geminiConfigured === null ? null : geminiConfigured ? (_jsxs("span", { className: "flex items-center gap-1 text-[10px] text-green-500", children: [_jsx(IconCheck, { size: 10 }), "Key set"] })) : (_jsxs("button", { type: "button", onClick: (e) => {
311
+ }, className: "inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-accent/40", children: "Connect Builder.io" })) }), _jsx(ProviderOption, { id: "gemini", selected: transcriptionMode === "batch" && provider === "gemini", onSelect: () => chooseBatchProvider("gemini"), title: "Google Gemini", subtitle: "BYOK Gemini for AI cleanup and optional strict batch transcription.", rightSlot: geminiConfigured === null ? null : geminiConfigured ? (_jsxs("span", { className: "flex items-center gap-1 text-[10px] text-green-500", children: [_jsx(IconCheck, { size: 10 }), "Key set"] })) : (_jsxs("button", { type: "button", onClick: (e) => {
300
312
  e.stopPropagation();
301
313
  focusKey("GEMINI_API_KEY");
302
314
  }, className: "inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-accent/40", children: ["Add key", _jsx(IconExternalLink, { size: 10 })] })) }), _jsx(ProviderOption, { id: "openai", selected: transcriptionMode === "batch" && provider === "openai", onSelect: () => chooseBatchProvider("openai"), title: "OpenAI Transcribe", subtitle: "Batch transcription provider. Requires an OpenAI API key.", rightSlot: openAiConfigured === null ? null : openAiConfigured ? (_jsxs("span", { className: "flex items-center gap-1 text-[10px] text-green-500", children: [_jsx(IconCheck, { size: 10 }), "Key set"] })) : (_jsxs("button", { type: "button", onClick: (e) => {
@@ -3,7 +3,7 @@ export type SettingsSectionId = "account" | "llm" | "app-models" | "limits" | "v
3
3
  export declare const SETTINGS_SECTION_IDS: Set<SettingsSectionId>;
4
4
  export declare const ALL_SETTINGS_SECTIONS: readonly SettingsSectionId[];
5
5
  export declare const AGENT_SETTINGS_SECTIONS: readonly SettingsSectionId[];
6
- export declare const CONNECTION_SETTINGS_SECTIONS: readonly SettingsSectionId[];
6
+ export declare const INTEGRATION_SETTINGS_SECTIONS: readonly SettingsSectionId[];
7
7
  export declare const WORKSPACE_SETTINGS_SECTIONS: readonly SettingsSectionId[];
8
8
  export declare function buildSectionSearchEntries(sections: readonly SettingsSectionId[]): SettingsSearchEntry[];
9
9
  export interface AgentSettingsSearchTab {
@@ -46,9 +46,9 @@ export const AGENT_SETTINGS_SECTIONS = [
46
46
  "background",
47
47
  "a2a",
48
48
  ];
49
- export const CONNECTION_SETTINGS_SECTIONS = [
50
- "secrets",
49
+ export const INTEGRATION_SETTINGS_SECTIONS = [
51
50
  "integrations",
51
+ "secrets",
52
52
  "email",
53
53
  "browser",
54
54
  "usage",
@@ -90,7 +90,7 @@ const SETTINGS_SECTION_SEARCH_META = {
90
90
  keywords: "triggers scheduled events cron jobs",
91
91
  },
92
92
  secrets: {
93
- label: "API Keys & Connections",
93
+ label: "API keys",
94
94
  keywords: "secrets credentials tokens api keys environment variables",
95
95
  },
96
96
  hosting: {
@@ -150,15 +150,15 @@ export function getAgentSettingsSearchTabs() {
150
150
  return [
151
151
  {
152
152
  id: "agent",
153
- label: "Manage agent",
153
+ label: "Agent",
154
154
  keywords: "agent workspace manage agent model llm limits voice automations",
155
155
  searchEntries: buildSectionSearchEntries(AGENT_SETTINGS_SECTIONS),
156
156
  },
157
157
  {
158
- id: "connections",
159
- label: "Connections",
160
- keywords: "connections secrets integrations email browser usage",
161
- searchEntries: buildSectionSearchEntries(CONNECTION_SETTINGS_SECTIONS),
158
+ id: "integrations",
159
+ label: "Integrations",
160
+ keywords: "integrations agent integrations connections secrets email browser usage tools",
161
+ searchEntries: buildSectionSearchEntries(INTEGRATION_SETTINGS_SECTIONS),
162
162
  },
163
163
  {
164
164
  id: "organization",
@@ -6,7 +6,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  *
7
7
  * Drop this next to transcript displays in any template.
8
8
  */
9
- import { IconBolt, IconExternalLink, IconLoader2 } from "@tabler/icons-react";
9
+ import { IconBolt, IconLoader2 } from "@tabler/icons-react";
10
10
  import { useCallback, useEffect, useRef, useState } from "react";
11
11
  import { agentNativePath } from "../api-path.js";
12
12
  import { openBuilderConnectPopup } from "../settings/useBuilderStatus.js";
@@ -82,6 +82,6 @@ export function BuilderTranscriptionCta() {
82
82
  return null;
83
83
  return (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-border/50 bg-muted/30 px-3 py-2 text-xs text-muted-foreground", children: [_jsx(IconBolt, { size: 14, className: "shrink-0 text-muted-foreground/70", "aria-hidden": "true" }), _jsx("span", { className: "flex-1", children: connecting
84
84
  ? "Waiting for Builder.io…"
85
- : "Connect Builder.io for higher-quality transcription — free credits, no API key needed." }), error ? (_jsx("span", { className: "text-destructive text-[10px]", children: error })) : connecting ? (_jsx(IconLoader2, { size: 12, className: "shrink-0 animate-spin" })) : (_jsxs("button", { type: "button", onClick: handleConnect, className: "ml-auto shrink-0 inline-flex items-center gap-1 rounded bg-foreground px-2 py-1 text-[10px] font-semibold text-background hover:opacity-90 transition-opacity", children: ["Connect", _jsx(IconExternalLink, { size: 10 })] }))] }));
85
+ : "Connect Builder.io for higher-quality transcription — free credits, no API key needed." }), error ? (_jsx("span", { className: "text-destructive text-[10px]", children: error })) : connecting ? (_jsx(IconLoader2, { size: 12, className: "shrink-0 animate-spin" })) : (_jsx("button", { type: "button", onClick: handleConnect, className: "ml-auto shrink-0 inline-flex items-center gap-1 rounded bg-foreground px-2 py-1 text-[10px] font-semibold text-background hover:opacity-90 transition-opacity", children: "Connect" }))] }));
86
86
  }
87
87
  //# sourceMappingURL=BuilderTranscriptionCta.js.map
@@ -13,9 +13,15 @@ export interface ChatThreadSummary {
13
13
  createdAt: number;
14
14
  updatedAt: number;
15
15
  scope: ChatThreadScope | null;
16
+ source?: ChatThreadSource | null;
16
17
  pinnedAt?: number | null;
17
18
  archivedAt?: number | null;
18
19
  }
20
+ export interface ChatThreadSource {
21
+ platform?: string;
22
+ appId?: string;
23
+ url?: string;
24
+ }
19
25
  export interface ChatThreadData {
20
26
  id: string;
21
27
  ownerEmail: string;
@@ -26,6 +32,7 @@ export interface ChatThreadData {
26
32
  createdAt: number;
27
33
  updatedAt: number;
28
34
  scope: ChatThreadScope | null;
35
+ source?: ChatThreadSource | null;
29
36
  pinnedAt?: number | null;
30
37
  archivedAt?: number | null;
31
38
  }
@@ -57,6 +64,8 @@ export interface UseChatThreadsOptions {
57
64
  * create/new-chat mode.
58
65
  */
59
66
  routeThreadId?: string | null;
67
+ /** Include connected and other-app chats in list/search results. */
68
+ includeExternal?: boolean;
60
69
  }
61
70
  export declare function useChatThreads(apiUrl?: string, storageKey?: string, scope?: ChatThreadScope | null, options?: UseChatThreadsOptions): {
62
71
  threads: ChatThreadSummary[];
@@ -3,8 +3,14 @@ import { agentNativePath } from "./api-path.js";
3
3
  const ACTIVE_THREAD_KEY = "agent-chat-active-thread";
4
4
  const THREADS_UPDATED_EVENT = "agent-chat:threads-updated";
5
5
  const THREADS_PAGE_SIZE = 50;
6
- async function fetchThreadListPage(apiUrl, offset) {
7
- return fetch(offset > 0 ? `${apiUrl}/threads?offset=${offset}` : `${apiUrl}/threads`).then(async (res) => {
6
+ async function fetchThreadListPage(apiUrl, offset, includeExternal) {
7
+ const params = new URLSearchParams();
8
+ if (offset > 0)
9
+ params.set("offset", String(offset));
10
+ if (includeExternal)
11
+ params.set("includeExternal", "1");
12
+ const query = params.toString();
13
+ return fetch(`${apiUrl}/threads${query ? `?${query}` : ""}`).then(async (res) => {
8
14
  if (!res.ok)
9
15
  return undefined;
10
16
  const data = await res.json();
@@ -102,6 +108,7 @@ function nextThreadTitle(currentTitle, incomingTitle, incomingPreview, source =
102
108
  export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-chat"), storageKey, scope, options) {
103
109
  const autoCreate = options?.autoCreate !== false;
104
110
  const restoreActiveThread = options?.restoreActiveThread !== false;
111
+ const includeExternal = options?.includeExternal === true;
105
112
  const routeControlsActiveThread = options?.routeThreadId !== undefined;
106
113
  const routeThreadId = normalizeThreadId(options?.routeThreadId);
107
114
  // Each (storageKey, scope) pair gets its own active-thread localStorage key
@@ -148,6 +155,7 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
148
155
  const [isLoadingMoreThreads, setIsLoadingMoreThreads] = useState(false);
149
156
  const [threadsLoadError, setThreadsLoadError] = useState(null);
150
157
  const nextThreadsOffsetRef = useRef(0);
158
+ const latestFetchRequestRef = useRef(0);
151
159
  const threadsRef = useRef(threads);
152
160
  threadsRef.current = threads;
153
161
  // IDs we generated client-side this session — consumers use this to know
@@ -341,9 +349,12 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
341
349
  threads,
342
350
  ]);
343
351
  const fetchThreads = useCallback(async (options) => {
352
+ const requestId = ++latestFetchRequestRef.current;
344
353
  try {
345
354
  const offset = options?.append ? nextThreadsOffsetRef.current : 0;
346
- const loaded = await fetchThreadListPage(apiUrl, offset);
355
+ const loaded = await fetchThreadListPage(apiUrl, offset, includeExternal);
356
+ if (requestId !== latestFetchRequestRef.current)
357
+ return undefined;
347
358
  if (!loaded) {
348
359
  if (!options?.append) {
349
360
  setThreadsLoadError("Could not load chat history.");
@@ -426,12 +437,14 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
426
437
  return loaded;
427
438
  }
428
439
  catch {
440
+ if (requestId !== latestFetchRequestRef.current)
441
+ return undefined;
429
442
  if (!options?.append) {
430
443
  setThreadsLoadError("Could not load chat history.");
431
444
  }
432
445
  return undefined;
433
446
  }
434
- }, [apiUrl]);
447
+ }, [apiUrl, includeExternal]);
435
448
  const loadMoreThreads = useCallback(async () => {
436
449
  if (isLoadingMoreThreads || !hasMoreThreads)
437
450
  return;
@@ -980,7 +993,10 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
980
993
  }, [apiUrl]);
981
994
  const searchThreads = useCallback(async (query) => {
982
995
  try {
983
- const res = await fetch(`${apiUrl}/threads?q=${encodeURIComponent(query)}`);
996
+ const params = new URLSearchParams({ q: query });
997
+ if (includeExternal)
998
+ params.set("includeExternal", "1");
999
+ const res = await fetch(`${apiUrl}/threads?${params.toString()}`);
984
1000
  if (!res.ok)
985
1001
  return [];
986
1002
  const data = await res.json();
@@ -989,7 +1005,7 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
989
1005
  catch {
990
1006
  return [];
991
1007
  }
992
- }, [apiUrl]);
1008
+ }, [apiUrl, includeExternal]);
993
1009
  const getThreadShareState = useCallback(async (threadId) => {
994
1010
  try {
995
1011
  const res = await fetch(`${apiUrl}/threads/${encodeURIComponent(threadId)}/share`);
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
62
62
  error: string;
63
63
  states?: undefined;
64
64
  } | {
65
- error?: undefined;
66
65
  states: {
67
66
  clientId: number;
68
67
  state: string;
69
68
  }[];
69
+ error?: undefined;
70
70
  }>>;
71
71
  /**
72
72
  * GET /_agent-native/collab/:docId/users
@@ -77,10 +77,10 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
77
77
  error: string;
78
78
  users?: undefined;
79
79
  } | {
80
- error?: undefined;
81
80
  users: {
82
81
  clientId: number;
83
82
  lastSeen: number;
84
83
  }[];
84
+ error?: undefined;
85
85
  }>>;
86
86
  //# sourceMappingURL=awareness.d.ts.map
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
26
26
  * Body: { update: string (base64), requestSource?: string }
27
27
  */
28
28
  export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
29
- error: string;
30
29
  ok?: undefined;
30
+ error: string;
31
31
  } | {
32
32
  error?: undefined;
33
33
  ok: boolean;
@@ -17,12 +17,12 @@ declare const _default: import("../../action.js").ActionDefinition<{
17
17
  id?: undefined;
18
18
  provider?: undefined;
19
19
  } | {
20
+ error?: undefined;
20
21
  configured?: undefined;
21
22
  connectPath?: undefined;
22
23
  url: string;
23
24
  id: string;
24
25
  provider: string;
25
- error?: undefined;
26
26
  }>;
27
27
  export default _default;
28
28
  //# sourceMappingURL=upload-image.d.ts.map
@@ -5,7 +5,7 @@ import { runAgentLoopDirectWithSoftTimeout } from "../agent/run-loop-with-resume
5
5
  import { startRun } from "../agent/run-manager.js";
6
6
  import { buildAssistantMessage, extractThreadMeta, } from "../agent/thread-data-builder.js";
7
7
  import { attachToolSearch } from "../agent/tool-search.js";
8
- import { createThread, getThread, updateThreadData, } from "../chat-threads/store.js";
8
+ import { createThread, getThread, setThreadSourceIfMissing, updateThreadData, } from "../chat-threads/store.js";
9
9
  import { resolveOrgIdForEmail } from "../org/context.js";
10
10
  import { runWithRequestContext } from "../server/request-context.js";
11
11
  import { getServiceAccountAccessToken, getServiceAccountEmail, getStartPageToken, googleDocsAdapter, listChanges, listDocComments, } from "./adapters/google-docs.js";
@@ -274,14 +274,22 @@ async function processComment(fileId, commentId, text, senderName, options, exis
274
274
  platformContext: { fileId, commentId },
275
275
  timestamp: Date.now(),
276
276
  };
277
+ const source = {
278
+ platform: PLATFORM,
279
+ url: `https://docs.google.com/document/d/${fileId}/edit`,
280
+ };
277
281
  let threadId = existingThreadId;
278
282
  if (!threadId) {
279
283
  const thread = await createThread(options.ownerEmail, {
280
284
  title: `Google Doc: ${senderName}`,
285
+ source,
281
286
  });
282
287
  await saveThreadMapping(PLATFORM, key, thread.id, { fileId, commentId });
283
288
  threadId = thread.id;
284
289
  }
290
+ // Older mapped conversations predate thread provenance. Backfill them as
291
+ // they are touched so Dispatch's default local-history view excludes them.
292
+ await setThreadSourceIfMissing(threadId, source);
285
293
  const thread = await getThread(threadId);
286
294
  const existingMessages = [];
287
295
  if (thread?.threadData) {
@@ -11,7 +11,7 @@ import { startRun, } from "../agent/run-manager.js";
11
11
  import { buildCurrentTimeUserContext, buildRuntimeContextPrompt, } from "../agent/runtime-context.js";
12
12
  import { buildAssistantMessage, extractThreadMeta, threadDataToEngineMessages, } from "../agent/thread-data-builder.js";
13
13
  import { attachToolSearch } from "../agent/tool-search.js";
14
- import { createThread, getThread, grantThreadUserShare, } from "../chat-threads/store.js";
14
+ import { createThread, getThread, grantThreadUserShare, setThreadSourceIfMissing, } from "../chat-threads/store.js";
15
15
  import { updateThreadData } from "../chat-threads/store.js";
16
16
  import { isLocalDatabase } from "../db/client.js";
17
17
  import { getOrgA2ASecret, resolveOrgIdForEmail } from "../org/context.js";
@@ -575,6 +575,11 @@ async function processIncomingMessage(incoming, options, opts = {}) {
575
575
  const threadOrgId = opts.orgId ?? (await resolveOrgIdForEmail(ownerEmail));
576
576
  const createdThread = await runWithRequestContext({ userEmail: ownerEmail, orgId: threadOrgId ?? undefined }, () => createThread(ownerEmail, {
577
577
  title: `${adapter.label}: ${incoming.senderName || incoming.senderId || "User"}`,
578
+ source: {
579
+ platform: incoming.platform,
580
+ appId: options.appId ?? null,
581
+ url: incoming.sourceUrl ?? null,
582
+ },
578
583
  }));
579
584
  await saveThreadMapping(incoming.platform, incoming.externalThreadId, createdThread.id, incoming.platformContext);
580
585
  mapping = {
@@ -587,6 +592,11 @@ async function processIncomingMessage(incoming, options, opts = {}) {
587
592
  };
588
593
  }
589
594
  threadId = mapping.internalThreadId;
595
+ await setThreadSourceIfMissing(threadId, {
596
+ platform: incoming.platform,
597
+ appId: options.appId ?? null,
598
+ url: incoming.sourceUrl ?? null,
599
+ });
590
600
  // Load existing thread history for context.
591
601
  thread = await getThread(threadId);
592
602
  }