@agent-native/core 0.119.2 → 0.119.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 (228) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +19 -0
  3. package/corpus/core/package.json +3 -3
  4. package/corpus/core/src/agent/production-agent.ts +107 -125
  5. package/corpus/core/src/agent/run-manager.ts +45 -2
  6. package/corpus/core/src/agent/thread-data-builder.ts +85 -8
  7. package/corpus/core/src/cli/create.ts +44 -5
  8. package/corpus/core/src/client/AssistantChat.tsx +37 -9
  9. package/corpus/core/src/client/chat/message-components.tsx +9 -1
  10. package/corpus/core/src/client/chat/repo-helpers.ts +77 -0
  11. package/corpus/core/src/client/chat/tool-call-display.tsx +51 -15
  12. package/corpus/core/src/server/agent-chat/framework-prompts.ts +3 -1
  13. package/corpus/core/src/server/agent-chat/plugin-options.ts +9 -0
  14. package/corpus/core/src/server/agent-chat-plugin.ts +12 -3
  15. package/corpus/core/src/templates/chat/.agents/skills/actions/SKILL.md +512 -0
  16. package/corpus/core/src/templates/chat/.agents/skills/adding-a-feature/SKILL.md +189 -0
  17. package/corpus/core/src/templates/chat/.agents/skills/agent-native-docs/SKILL.md +115 -0
  18. package/corpus/core/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +177 -0
  19. package/corpus/core/src/templates/chat/.agents/skills/capture-learnings/SKILL.md +89 -0
  20. package/corpus/core/src/templates/chat/.agents/skills/create-skill/SKILL.md +221 -0
  21. package/corpus/core/src/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +220 -0
  22. package/corpus/core/src/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +263 -0
  23. package/corpus/core/src/templates/chat/.agents/skills/feature-flags/SKILL.md +169 -0
  24. package/corpus/core/src/templates/chat/.agents/skills/frontend-design/SKILL.md +160 -0
  25. package/corpus/core/src/templates/chat/.agents/skills/real-time-sync/SKILL.md +232 -0
  26. package/corpus/core/src/templates/chat/.agents/skills/security/SKILL.md +280 -0
  27. package/corpus/core/src/templates/chat/.agents/skills/self-modifying-code/SKILL.md +118 -0
  28. package/corpus/core/src/templates/chat/.agents/skills/shadcn-ui/SKILL.md +119 -0
  29. package/corpus/core/src/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +100 -0
  30. package/corpus/core/src/templates/chat/.env.example +5 -0
  31. package/corpus/core/src/templates/chat/.ignore +0 -0
  32. package/corpus/core/src/templates/chat/.oxfmtrc.json +8 -0
  33. package/corpus/core/src/templates/chat/AGENTS.md +69 -0
  34. package/corpus/core/src/templates/chat/CHANGELOG.md +10 -0
  35. package/corpus/core/src/templates/chat/DEVELOPING.md +185 -0
  36. package/corpus/core/src/templates/chat/README.md +32 -0
  37. package/corpus/core/src/templates/chat/_gitignore +43 -0
  38. package/corpus/core/src/templates/chat/actions/hello.ts +13 -0
  39. package/corpus/core/src/templates/chat/actions/navigate.ts +41 -0
  40. package/corpus/core/src/templates/chat/actions/run.ts +2 -0
  41. package/corpus/core/src/templates/chat/actions/view-screen.ts +31 -0
  42. package/corpus/core/src/templates/chat/app/components/layout/Header.tsx +60 -0
  43. package/corpus/core/src/templates/chat/app/components/layout/Layout.tsx +185 -0
  44. package/corpus/core/src/templates/chat/app/components/layout/Sidebar.tsx +450 -0
  45. package/corpus/core/src/templates/chat/app/components/ui/button.tsx +1 -0
  46. package/corpus/core/src/templates/chat/app/components/ui/card.tsx +1 -0
  47. package/corpus/core/src/templates/chat/app/components/ui/dropdown-menu.tsx +1 -0
  48. package/corpus/core/src/templates/chat/app/components/ui/input.tsx +1 -0
  49. package/corpus/core/src/templates/chat/app/components/ui/label.tsx +1 -0
  50. package/corpus/core/src/templates/chat/app/components/ui/sheet.tsx +1 -0
  51. package/corpus/core/src/templates/chat/app/components/ui/toolkit-provider.tsx +17 -0
  52. package/corpus/core/src/templates/chat/app/components/ui/tooltip.tsx +1 -0
  53. package/corpus/core/src/templates/chat/app/design-system.ts +3 -0
  54. package/corpus/core/src/templates/chat/app/entry.client.tsx +19 -0
  55. package/corpus/core/src/templates/chat/app/entry.server.tsx +10 -0
  56. package/corpus/core/src/templates/chat/app/global.css +93 -0
  57. package/corpus/core/src/templates/chat/app/hooks/use-navigation-state.ts +93 -0
  58. package/corpus/core/src/templates/chat/app/i18n/ar-SA.ts +68 -0
  59. package/corpus/core/src/templates/chat/app/i18n/de-DE.ts +71 -0
  60. package/corpus/core/src/templates/chat/app/i18n/en-US.ts +70 -0
  61. package/corpus/core/src/templates/chat/app/i18n/es-ES.ts +71 -0
  62. package/corpus/core/src/templates/chat/app/i18n/fr-FR.ts +71 -0
  63. package/corpus/core/src/templates/chat/app/i18n/hi-IN.ts +69 -0
  64. package/corpus/core/src/templates/chat/app/i18n/index.ts +34 -0
  65. package/corpus/core/src/templates/chat/app/i18n/ja-JP.ts +70 -0
  66. package/corpus/core/src/templates/chat/app/i18n/ko-KR.ts +70 -0
  67. package/corpus/core/src/templates/chat/app/i18n/pt-BR.ts +71 -0
  68. package/corpus/core/src/templates/chat/app/i18n/zh-CN.ts +66 -0
  69. package/corpus/core/src/templates/chat/app/i18n/zh-TW.ts +66 -0
  70. package/corpus/core/src/templates/chat/app/i18n-data.ts +556 -0
  71. package/corpus/core/src/templates/chat/app/lib/agent-page.tsx +49 -0
  72. package/corpus/core/src/templates/chat/app/lib/app-config.ts +11 -0
  73. package/corpus/core/src/templates/chat/app/lib/tab-id.ts +1 -0
  74. package/corpus/core/src/templates/chat/app/lib/utils.ts +1 -0
  75. package/corpus/core/src/templates/chat/app/root.tsx +173 -0
  76. package/corpus/core/src/templates/chat/app/routes/_index.tsx +94 -0
  77. package/corpus/core/src/templates/chat/app/routes/agent.tsx +24 -0
  78. package/corpus/core/src/templates/chat/app/routes/chat.$threadId.tsx +1 -0
  79. package/corpus/core/src/templates/chat/app/routes/database.tsx +17 -0
  80. package/corpus/core/src/templates/chat/app/routes/extensions.$id.$slug.tsx +2 -0
  81. package/corpus/core/src/templates/chat/app/routes/extensions.$id.tsx +11 -0
  82. package/corpus/core/src/templates/chat/app/routes/extensions._index.tsx +11 -0
  83. package/corpus/core/src/templates/chat/app/routes/extensions.tsx +5 -0
  84. package/corpus/core/src/templates/chat/app/routes/observability.tsx +19 -0
  85. package/corpus/core/src/templates/chat/app/routes/settings.tsx +89 -0
  86. package/corpus/core/src/templates/chat/app/routes/team.tsx +11 -0
  87. package/corpus/core/src/templates/chat/app/routes.ts +4 -0
  88. package/corpus/core/src/templates/chat/app/vite-env.d.ts +6 -0
  89. package/corpus/core/src/templates/chat/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
  90. package/corpus/core/src/templates/chat/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  91. package/corpus/core/src/templates/chat/changelog/2026-06-27-traditional-chinese-copy-uses-taiwan-terminology.md +6 -0
  92. package/corpus/core/src/templates/chat/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
  93. package/corpus/core/src/templates/chat/changelog/2026-06-29-chat-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  94. package/corpus/core/src/templates/chat/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  95. package/corpus/core/src/templates/chat/changelog/2026-07-10-chat-now-makes-ai-connection-setup-clear-without-shifting-th.md +6 -0
  96. package/corpus/core/src/templates/chat/changelog/2026-07-13-a-full-agent-page-now-brings-context-files-connections-jobs-.md +6 -0
  97. package/corpus/core/src/templates/chat/changelog/2026-07-14-chat-opens-reliably-on-hosted-deployments-instead-of-failing.md +6 -0
  98. package/corpus/core/src/templates/chat/changelog/2026-07-14-fixed-chat-template-startup-with-older-core-versions.md +6 -0
  99. package/corpus/core/src/templates/chat/changelog/2026-07-15-chat-navigation-focuses-on-chat-and-agent.md +6 -0
  100. package/corpus/core/src/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
  101. package/corpus/core/src/templates/chat/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  102. package/corpus/core/src/templates/chat/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  103. package/corpus/core/src/templates/chat/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
  104. package/corpus/core/src/templates/chat/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +6 -0
  105. package/corpus/core/src/templates/chat/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +6 -0
  106. package/corpus/core/src/templates/chat/components.json +20 -0
  107. package/corpus/core/src/templates/chat/learnings.defaults.md +5 -0
  108. package/corpus/core/src/templates/chat/netlify.toml +10 -0
  109. package/corpus/core/src/templates/chat/package.json +92 -0
  110. package/corpus/core/src/templates/chat/public/agent-native-icon-dark.svg +10 -0
  111. package/corpus/core/src/templates/chat/public/agent-native-icon-light.svg +10 -0
  112. package/corpus/core/src/templates/chat/public/agent-native-logo-dark.svg +21 -0
  113. package/corpus/core/src/templates/chat/public/agent-native-logo-light.svg +21 -0
  114. package/corpus/core/src/templates/chat/public/favicon.svg +1 -0
  115. package/corpus/core/src/templates/chat/public/icon-180.svg +1 -0
  116. package/corpus/core/src/templates/chat/public/icon-192.svg +1 -0
  117. package/corpus/core/src/templates/chat/public/icon-512.svg +1 -0
  118. package/corpus/core/src/templates/chat/public/manifest.json +21 -0
  119. package/corpus/core/src/templates/chat/react-router.config.ts +7 -0
  120. package/corpus/core/src/templates/chat/server/middleware/auth.ts +15 -0
  121. package/corpus/core/src/templates/chat/server/plugins/agent-chat.ts +21 -0
  122. package/corpus/core/src/templates/chat/server/plugins/auth.ts +17 -0
  123. package/corpus/core/src/templates/chat/server/routes/[...page].get.ts +5 -0
  124. package/corpus/core/src/templates/chat/ssr-entry.ts +15 -0
  125. package/corpus/core/src/templates/chat/tsconfig.json +21 -0
  126. package/corpus/core/src/templates/chat/vite.config.ts +19 -0
  127. package/corpus/templates/analytics/AGENTS.md +19 -11
  128. package/corpus/templates/analytics/actions/list-data-dictionary.ts +3 -1
  129. package/corpus/templates/analytics/actions/save-data-dictionary-entry.ts +14 -0
  130. package/corpus/templates/analytics/actions/search-analytics-query-catalog.ts +41 -0
  131. package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +13 -0
  132. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +12 -11
  133. package/corpus/templates/analytics/app/pages/adhoc/DashboardSkeleton.tsx +3 -3
  134. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +7 -1
  135. package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +8 -1
  136. package/corpus/templates/analytics/changelog/2026-07-22-fixed-dashboards-and-analyses-missing-a-copyable-share-link.md +6 -0
  137. package/corpus/templates/analytics/changelog/2026-07-23-dashboard-chart-loading-placeholders-are-easier-to-see-in-da.md +6 -0
  138. package/corpus/templates/analytics/changelog/2026-07-23-routine-analytics-lookups-reuse-existing-query-definitions.md +6 -0
  139. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +1 -12
  140. package/corpus/templates/analytics/server/lib/analytics-query-catalog.ts +405 -0
  141. package/corpus/templates/analytics/server/plugins/agent-chat.ts +11 -16
  142. package/corpus/templates/content/changelog/2026-07-23-database-toolbar-sort-and-filter-controls-open-reliably.md +6 -0
  143. package/corpus/templates/forms/app/lib/public-form-link.ts +14 -0
  144. package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +6 -14
  145. package/corpus/templates/forms/changelog/2026-07-23-published-form-links-can-be-copied-without-being-asked-to-re.md +6 -0
  146. package/corpus/templates/mail/changelog/2026-07-23-scheduled-emails-now-send-only-once-when-multiple-requests-r.md +6 -0
  147. package/corpus/templates/mail/server/lib/jobs.ts +13 -4
  148. package/corpus/templates/mail/server/plugins/mail-jobs.ts +1 -1
  149. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +7 -18
  150. package/corpus/templates/slides/app/lib/agent-visible-message.ts +28 -0
  151. package/corpus/templates/slides/app/pages/Index.tsx +6 -34
  152. package/corpus/templates/slides/changelog/2026-07-23-full-prompts-stay-visible-in-chat-when-creating-decks-or-add.md +6 -0
  153. package/corpus/toolkit/CHANGELOG.md +13 -0
  154. package/corpus/toolkit/package.json +1 -1
  155. package/corpus/toolkit/src/editor/SharedRichEditor.tsx +23 -2
  156. package/corpus/toolkit/src/editor/SlashCommandMenu.tsx +68 -2
  157. package/corpus/toolkit/src/editor/index.ts +1 -0
  158. package/corpus/toolkit/src/editor.css +75 -0
  159. package/corpus/toolkit/src/ui/button.tsx +3 -2
  160. package/dist/agent/production-agent.d.ts.map +1 -1
  161. package/dist/agent/production-agent.js +96 -115
  162. package/dist/agent/production-agent.js.map +1 -1
  163. package/dist/agent/run-manager.d.ts +9 -1
  164. package/dist/agent/run-manager.d.ts.map +1 -1
  165. package/dist/agent/run-manager.js +26 -0
  166. package/dist/agent/run-manager.js.map +1 -1
  167. package/dist/agent/thread-data-builder.d.ts +2 -0
  168. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  169. package/dist/agent/thread-data-builder.js +69 -9
  170. package/dist/agent/thread-data-builder.js.map +1 -1
  171. package/dist/cli/create.d.ts +2 -1
  172. package/dist/cli/create.d.ts.map +1 -1
  173. package/dist/cli/create.js +43 -6
  174. package/dist/cli/create.js.map +1 -1
  175. package/dist/client/AssistantChat.d.ts.map +1 -1
  176. package/dist/client/AssistantChat.js +32 -10
  177. package/dist/client/AssistantChat.js.map +1 -1
  178. package/dist/client/chat/message-components.d.ts +2 -1
  179. package/dist/client/chat/message-components.d.ts.map +1 -1
  180. package/dist/client/chat/message-components.js +9 -3
  181. package/dist/client/chat/message-components.js.map +1 -1
  182. package/dist/client/chat/repo-helpers.d.ts +4 -0
  183. package/dist/client/chat/repo-helpers.d.ts.map +1 -1
  184. package/dist/client/chat/repo-helpers.js +61 -0
  185. package/dist/client/chat/repo-helpers.js.map +1 -1
  186. package/dist/client/chat/tool-call-display.d.ts +2 -1
  187. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  188. package/dist/client/chat/tool-call-display.js +10 -9
  189. package/dist/client/chat/tool-call-display.js.map +1 -1
  190. package/dist/collab/awareness.d.ts +2 -2
  191. package/dist/collab/awareness.d.ts.map +1 -1
  192. package/dist/collab/routes.d.ts +1 -1
  193. package/dist/collab/struct-routes.d.ts +1 -1
  194. package/dist/notifications/routes.d.ts +2 -2
  195. package/dist/observability/routes.d.ts +3 -3
  196. package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
  197. package/dist/provider-api/actions/provider-api.d.ts +7 -7
  198. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  199. package/dist/resources/handlers.d.ts +1 -1
  200. package/dist/secrets/routes.d.ts +9 -9
  201. package/dist/server/agent-chat/framework-prompts.d.ts.map +1 -1
  202. package/dist/server/agent-chat/framework-prompts.js +3 -1
  203. package/dist/server/agent-chat/framework-prompts.js.map +1 -1
  204. package/dist/server/agent-chat/plugin-options.d.ts +9 -0
  205. package/dist/server/agent-chat/plugin-options.d.ts.map +1 -1
  206. package/dist/server/agent-chat/plugin-options.js.map +1 -1
  207. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  208. package/dist/server/agent-chat-plugin.js +11 -2
  209. package/dist/server/agent-chat-plugin.js.map +1 -1
  210. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  211. package/dist/server/realtime-token.d.ts +1 -1
  212. package/dist/server/transcribe-voice.d.ts +1 -1
  213. package/package.json +4 -4
  214. package/src/agent/production-agent.ts +107 -125
  215. package/src/agent/run-manager.ts +45 -2
  216. package/src/agent/thread-data-builder.ts +85 -8
  217. package/src/cli/create.ts +44 -5
  218. package/src/client/AssistantChat.tsx +37 -9
  219. package/src/client/chat/message-components.tsx +9 -1
  220. package/src/client/chat/repo-helpers.ts +77 -0
  221. package/src/client/chat/tool-call-display.tsx +51 -15
  222. package/src/server/agent-chat/framework-prompts.ts +3 -1
  223. package/src/server/agent-chat/plugin-options.ts +9 -0
  224. package/src/server/agent-chat-plugin.ts +12 -3
  225. package/src/templates/chat/actions/hello.spec.ts +0 -23
  226. package/src/templates/chat/actions/navigate.spec.ts +0 -68
  227. package/src/templates/chat/actions/view-screen.spec.ts +0 -40
  228. package/src/templates/chat/app/lib/agent-page.spec.tsx +0 -61
@@ -0,0 +1,450 @@
1
+ import {
2
+ navigateWithAgentChatViewTransition,
3
+ useChatThreads,
4
+ type ChatThreadSummary,
5
+ } from "@agent-native/core/client/agent-chat";
6
+ import { appPath } from "@agent-native/core/client/api-path";
7
+ import { ExtensionsSidebarSection } from "@agent-native/core/client/extensions";
8
+ import { useT } from "@agent-native/core/client/i18n";
9
+ import { OrgSwitcher } from "@agent-native/core/client/org";
10
+ import { FeedbackButton } from "@agent-native/core/client/ui";
11
+ import {
12
+ ChatHistoryRail,
13
+ type ChatHistoryItem,
14
+ } from "@agent-native/toolkit/chat-history";
15
+ import {
16
+ IconHierarchy2,
17
+ IconLayoutSidebarLeftCollapse,
18
+ IconLayoutSidebarLeftExpand,
19
+ IconMessageCircle,
20
+ IconSettings,
21
+ } from "@tabler/icons-react";
22
+ import { useEffect, useMemo } from "react";
23
+ import { Link, useLocation, useNavigate } from "react-router";
24
+ import { toast } from "sonner";
25
+
26
+ import {
27
+ Tooltip,
28
+ TooltipContent,
29
+ TooltipTrigger,
30
+ } from "@/components/ui/tooltip";
31
+ import { APP_TITLE } from "@/lib/app-config";
32
+ import { cn } from "@/lib/utils";
33
+
34
+ const navItems = [
35
+ {
36
+ icon: IconMessageCircle,
37
+ labelKey: "navigation.chat",
38
+ href: "/",
39
+ view: "chat",
40
+ },
41
+ {
42
+ icon: IconHierarchy2,
43
+ labelKey: "settings.agentTitle",
44
+ href: "/agent",
45
+ view: "agent",
46
+ },
47
+ {
48
+ icon: IconSettings,
49
+ labelKey: "navigation.settings",
50
+ href: "/settings",
51
+ view: "settings",
52
+ },
53
+ ];
54
+
55
+ const CHAT_STORAGE_KEY = "chat";
56
+ const CHAT_ACTIVE_THREAD_KEY = `agent-chat-active-thread:${CHAT_STORAGE_KEY}`;
57
+
58
+ interface SidebarProps {
59
+ collapsed?: boolean;
60
+ collapsible?: boolean;
61
+ onCollapsedChange?: (collapsed: boolean) => void;
62
+ }
63
+
64
+ function formatThreadAge(updatedAt: number) {
65
+ const diffMs = Math.max(0, Date.now() - updatedAt);
66
+ const minutes = Math.floor(diffMs / 60_000);
67
+ if (minutes < 1) return "now";
68
+ if (minutes < 60) return `${minutes}m`;
69
+ const hours = Math.floor(minutes / 60);
70
+ if (hours < 24) return `${hours}h`;
71
+ const days = Math.floor(hours / 24);
72
+ if (days < 7) return `${days}d`;
73
+ return new Date(updatedAt).toLocaleDateString([], {
74
+ month: "short",
75
+ day: "numeric",
76
+ });
77
+ }
78
+
79
+ function threadTitle(thread: ChatThreadSummary) {
80
+ return thread.title || thread.preview || "Untitled chat";
81
+ }
82
+
83
+ function threadUpdatedAt(thread: ChatThreadSummary) {
84
+ return Number.isFinite(thread.updatedAt)
85
+ ? thread.updatedAt
86
+ : Number.isFinite(thread.createdAt)
87
+ ? thread.createdAt
88
+ : 0;
89
+ }
90
+
91
+ function compareThreads(a: ChatThreadSummary, b: ChatThreadSummary) {
92
+ const aPinned = a.pinnedAt ?? 0;
93
+ const bPinned = b.pinnedAt ?? 0;
94
+ if (aPinned || bPinned) return bPinned - aPinned;
95
+ return threadUpdatedAt(b) - threadUpdatedAt(a);
96
+ }
97
+
98
+ function persistedActiveThreadId() {
99
+ try {
100
+ return localStorage.getItem(CHAT_ACTIVE_THREAD_KEY);
101
+ } catch {
102
+ return null;
103
+ }
104
+ }
105
+
106
+ function persistActiveThreadId(threadId: string) {
107
+ try {
108
+ localStorage.setItem(CHAT_ACTIVE_THREAD_KEY, threadId);
109
+ } catch {}
110
+ }
111
+
112
+ function threadIdFromPath(pathname: string) {
113
+ const match = pathname.match(/^\/chat\/([^/]+)/);
114
+ if (!match) return null;
115
+ try {
116
+ const value = decodeURIComponent(match[1]).trim();
117
+ return value || null;
118
+ } catch {
119
+ return null;
120
+ }
121
+ }
122
+
123
+ function chatThreadPath(threadId: string) {
124
+ return `/chat/${encodeURIComponent(threadId)}`;
125
+ }
126
+
127
+ function ChatThreadsSection() {
128
+ const navigate = useNavigate();
129
+ const location = useLocation();
130
+ const t = useT();
131
+ const {
132
+ threads,
133
+ activeThreadId,
134
+ createThread,
135
+ switchThread,
136
+ pinThread,
137
+ archiveThread,
138
+ renameThread,
139
+ refreshThreads,
140
+ } = useChatThreads(undefined, CHAT_STORAGE_KEY, undefined, {
141
+ autoCreate: false,
142
+ restoreActiveThread: false,
143
+ });
144
+
145
+ const visibleThreads = useMemo(
146
+ () =>
147
+ threads
148
+ .filter((thread) => thread.messageCount > 0 && !thread.archivedAt)
149
+ .sort(compareThreads)
150
+ .slice(0, 15),
151
+ [threads],
152
+ );
153
+ const displayedActiveThreadId =
154
+ threadIdFromPath(location.pathname) ??
155
+ (location.pathname === "/" ? null : activeThreadId);
156
+ const chatItems = useMemo<ChatHistoryItem[]>(
157
+ () =>
158
+ visibleThreads.map((thread) => ({
159
+ id: thread.id,
160
+ title: threadTitle(thread),
161
+ titleText: threadTitle(thread),
162
+ timestamp:
163
+ thread.id === displayedActiveThreadId
164
+ ? undefined
165
+ : formatThreadAge(threadUpdatedAt(thread)),
166
+ pinned: Boolean(thread.pinnedAt),
167
+ })),
168
+ [displayedActiveThreadId, visibleThreads],
169
+ );
170
+
171
+ useEffect(() => {
172
+ const refresh = () => refreshThreads();
173
+ const handleRunning = (event: Event) => {
174
+ const detail = (event as CustomEvent).detail as
175
+ | { isRunning?: unknown }
176
+ | undefined;
177
+ if (typeof detail?.isRunning === "boolean") refreshThreads();
178
+ };
179
+
180
+ window.addEventListener("agent-chat:threads-updated", refresh);
181
+ window.addEventListener("agentNative.chatRunning", handleRunning);
182
+ window.addEventListener("focus", refresh);
183
+ return () => {
184
+ window.removeEventListener("agent-chat:threads-updated", refresh);
185
+ window.removeEventListener("agentNative.chatRunning", handleRunning);
186
+ window.removeEventListener("focus", refresh);
187
+ };
188
+ }, [refreshThreads]);
189
+
190
+ function openThread(threadId: string, options?: { isNew?: boolean }) {
191
+ switchThread(threadId);
192
+ persistActiveThreadId(threadId);
193
+ navigateWithAgentChatViewTransition(
194
+ navigate,
195
+ options?.isNew ? "/" : chatThreadPath(threadId),
196
+ );
197
+ window.requestAnimationFrame(() => {
198
+ window.dispatchEvent(
199
+ new CustomEvent("agent-chat:open-thread", {
200
+ detail: { threadId, newThread: options?.isNew === true },
201
+ }),
202
+ );
203
+ });
204
+ }
205
+
206
+ async function handleNewChat() {
207
+ const threadId = await createThread();
208
+ if (threadId) openThread(threadId, { isNew: true });
209
+ }
210
+
211
+ async function handleArchiveThread(threadId: string) {
212
+ const wasActive =
213
+ threadId === activeThreadId || threadId === persistedActiveThreadId();
214
+ const archived = await archiveThread(threadId);
215
+ if (!archived) {
216
+ toast.error(t("chat.archiveFailed"));
217
+ return;
218
+ }
219
+ if (wasActive) {
220
+ await handleNewChat();
221
+ }
222
+ }
223
+
224
+ function handleRenameThread(threadId: string, title: string) {
225
+ void renameThread(threadId, title).then((renamed) => {
226
+ if (!renamed) toast.error(t("chat.renameFailed"));
227
+ });
228
+ }
229
+
230
+ return (
231
+ <div className="mt-2 ms-4">
232
+ <ChatHistoryRail
233
+ items={chatItems}
234
+ activeId={displayedActiveThreadId}
235
+ onSelect={(threadId) => openThread(threadId)}
236
+ onNewChat={() => void handleNewChat()}
237
+ railLabels={{
238
+ newChat: t("chat.newChat"),
239
+ showMore: t("chat.chats"),
240
+ showLess: t("chat.chats"),
241
+ }}
242
+ renameMaxLength={160}
243
+ onTogglePin={(threadId) => {
244
+ const thread = visibleThreads.find((item) => item.id === threadId);
245
+ if (thread) void pinThread(threadId, !thread.pinnedAt);
246
+ }}
247
+ onRename={handleRenameThread}
248
+ onDelete={(threadId) => void handleArchiveThread(threadId)}
249
+ labels={{
250
+ options: (item) =>
251
+ t("chat.optionsFor", { title: item.titleText ?? "" }),
252
+ renameInput: (item) =>
253
+ t("chat.renameThread", { title: item.titleText ?? "" }),
254
+ rename: t("chat.renameChat"),
255
+ pin: t("chat.pinChat"),
256
+ unpin: t("chat.unpinChat"),
257
+ delete: t("chat.archiveChat"),
258
+ }}
259
+ className="min-w-0"
260
+ />
261
+ </div>
262
+ );
263
+ }
264
+
265
+ export function Sidebar({
266
+ collapsed = false,
267
+ collapsible = true,
268
+ onCollapsedChange,
269
+ }: SidebarProps) {
270
+ const location = useLocation();
271
+ const navigate = useNavigate();
272
+ const t = useT();
273
+ const isChatRoute =
274
+ location.pathname === "/" || location.pathname.startsWith("/chat/");
275
+ const ToggleIcon = collapsed
276
+ ? IconLayoutSidebarLeftExpand
277
+ : IconLayoutSidebarLeftCollapse;
278
+ const navClass = ({ isActive }: { isActive: boolean }) =>
279
+ cn(
280
+ "flex items-center text-sm transition-colors",
281
+ collapsed
282
+ ? "relative h-10 w-full justify-center rounded-none border-s-2 px-0"
283
+ : "h-9 rounded-md gap-3 px-3",
284
+ isActive
285
+ ? collapsed
286
+ ? "border-s-sidebar-accent-foreground/80 bg-sidebar-accent text-sidebar-accent-foreground"
287
+ : "bg-sidebar-accent text-sidebar-accent-foreground"
288
+ : collapsed
289
+ ? "border-s-transparent text-sidebar-foreground/70 hover:bg-sidebar-accent/55 hover:text-sidebar-accent-foreground"
290
+ : "text-sidebar-foreground hover:bg-sidebar-accent/65 hover:text-sidebar-accent-foreground",
291
+ );
292
+ const collapseButton = collapsible ? (
293
+ <Tooltip>
294
+ <TooltipTrigger asChild>
295
+ <button
296
+ type="button"
297
+ onClick={() => onCollapsedChange?.(!collapsed)}
298
+ className={cn(
299
+ "flex shrink-0 items-center justify-center rounded-md text-sidebar-foreground/65 transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
300
+ collapsed ? "size-8" : "size-7",
301
+ )}
302
+ aria-label={
303
+ collapsed
304
+ ? t("navigation.expandSidebar")
305
+ : t("navigation.collapseSidebar")
306
+ }
307
+ >
308
+ <ToggleIcon className="size-4" />
309
+ </button>
310
+ </TooltipTrigger>
311
+ <TooltipContent side="right">
312
+ {collapsed
313
+ ? t("navigation.expandSidebar")
314
+ : t("navigation.collapseSidebar")}
315
+ </TooltipContent>
316
+ </Tooltip>
317
+ ) : null;
318
+
319
+ return (
320
+ <aside
321
+ data-collapsed={collapsed ? "true" : "false"}
322
+ className={cn(
323
+ "flex h-full min-w-0 shrink-0 flex-col overflow-hidden border-e border-sidebar-border bg-sidebar text-sidebar-foreground transition-[width] duration-200 ease-out",
324
+ collapsed ? "w-12" : "w-60",
325
+ )}
326
+ >
327
+ <div
328
+ className={cn(
329
+ "flex shrink-0 items-center border-b border-sidebar-border",
330
+ collapsed ? "h-12 justify-center px-0" : "h-14 px-3",
331
+ )}
332
+ >
333
+ <Link
334
+ to="/"
335
+ className={cn(
336
+ "flex min-w-0 items-center rounded outline-none focus-visible:ring-2 focus-visible:ring-ring",
337
+ collapsed ? "size-7 justify-center" : "flex-1 gap-3",
338
+ )}
339
+ aria-label={collapsed ? APP_TITLE : undefined}
340
+ >
341
+ <img
342
+ src={appPath("/agent-native-icon-light.svg")}
343
+ alt=""
344
+ aria-hidden="true"
345
+ className="block h-4 w-auto shrink-0 dark:hidden"
346
+ />
347
+ <img
348
+ src={appPath("/agent-native-icon-dark.svg")}
349
+ alt=""
350
+ aria-hidden="true"
351
+ className="hidden h-4 w-auto shrink-0 dark:block"
352
+ />
353
+ <div className={cn("min-w-0", collapsed && "sr-only")}>
354
+ <p className="truncate text-sm font-semibold text-sidebar-accent-foreground">
355
+ {APP_TITLE}
356
+ </p>
357
+ </div>
358
+ </Link>
359
+ </div>
360
+
361
+ <nav
362
+ className={cn(
363
+ "flex-1 overflow-y-auto",
364
+ collapsed ? "px-0 py-2" : "px-2 py-3",
365
+ )}
366
+ >
367
+ <div className={cn("grid", collapsed ? "gap-0" : "gap-1")}>
368
+ {navItems.map((item) => {
369
+ const Icon = item.icon;
370
+ const isActive =
371
+ item.href === "/"
372
+ ? isChatRoute
373
+ : location.pathname.startsWith(item.href);
374
+ const link = (
375
+ <Link
376
+ to={item.href}
377
+ onClick={(event) => {
378
+ if (
379
+ item.href === "/" &&
380
+ !isChatRoute &&
381
+ !event.metaKey &&
382
+ !event.ctrlKey &&
383
+ !event.shiftKey &&
384
+ !event.altKey
385
+ ) {
386
+ event.preventDefault();
387
+ navigateWithAgentChatViewTransition(navigate, "/");
388
+ }
389
+ }}
390
+ className={navClass({ isActive })}
391
+ aria-current={isActive ? "page" : undefined}
392
+ aria-label={collapsed ? t(item.labelKey) : undefined}
393
+ >
394
+ <Icon className="size-4 shrink-0" />
395
+ <span className={collapsed ? "sr-only" : "truncate"}>
396
+ {t(item.labelKey)}
397
+ </span>
398
+ </Link>
399
+ );
400
+ return (
401
+ <div key={item.href}>
402
+ {collapsed ? (
403
+ <Tooltip>
404
+ <TooltipTrigger asChild>{link}</TooltipTrigger>
405
+ <TooltipContent side="right">
406
+ {t(item.labelKey)}
407
+ </TooltipContent>
408
+ </Tooltip>
409
+ ) : (
410
+ link
411
+ )}
412
+ {!collapsed && item.view === "chat" && isChatRoute ? (
413
+ <ChatThreadsSection />
414
+ ) : null}
415
+ </div>
416
+ );
417
+ })}
418
+ </div>
419
+ </nav>
420
+
421
+ <div className={cn("mt-auto shrink-0", collapsed && "py-2")}>
422
+ {!collapsed ? (
423
+ <div className="px-2 py-1">
424
+ <ExtensionsSidebarSection />
425
+ </div>
426
+ ) : null}
427
+
428
+ <div className={cn(collapsed ? "px-1 py-1" : "px-3 py-2")}>
429
+ <OrgSwitcher
430
+ reserveSpace
431
+ className={
432
+ collapsed
433
+ ? "h-8 justify-center px-0 [&>span]:sr-only [&>svg:last-child]:hidden"
434
+ : undefined
435
+ }
436
+ />
437
+ </div>
438
+
439
+ {!collapsed ? (
440
+ <div className="flex items-center justify-end gap-1 px-3 py-2">
441
+ <FeedbackButton className="min-w-0 flex-1" side="right" />
442
+ {collapseButton}
443
+ </div>
444
+ ) : collapseButton ? (
445
+ <div className="flex justify-center px-1 py-1">{collapseButton}</div>
446
+ ) : null}
447
+ </div>
448
+ </aside>
449
+ );
450
+ }
@@ -0,0 +1 @@
1
+ export * from "@agent-native/toolkit/ui/button";
@@ -0,0 +1 @@
1
+ export * from "@agent-native/toolkit/ui/card";
@@ -0,0 +1 @@
1
+ export * from "@agent-native/toolkit/ui/dropdown-menu";
@@ -0,0 +1 @@
1
+ export * from "@agent-native/toolkit/ui/input";
@@ -0,0 +1 @@
1
+ export * from "@agent-native/toolkit/ui/label";
@@ -0,0 +1 @@
1
+ export * from "@agent-native/toolkit/ui/sheet";
@@ -0,0 +1,17 @@
1
+ import { ToolkitProvider, type ToolkitComponents } from "@agent-native/toolkit";
2
+ import type { ReactNode } from "react";
3
+
4
+ import { Button } from "@/components/ui/button";
5
+ import { designSystem } from "@/design-system";
6
+
7
+ const components: ToolkitComponents = {
8
+ Button: Button as ToolkitComponents["Button"],
9
+ };
10
+
11
+ export function AppToolkitProvider({ children }: { children: ReactNode }) {
12
+ return (
13
+ <ToolkitProvider components={components} designSystem={designSystem}>
14
+ {children}
15
+ </ToolkitProvider>
16
+ );
17
+ }
@@ -0,0 +1 @@
1
+ export * from "@agent-native/toolkit/ui/tooltip";
@@ -0,0 +1,3 @@
1
+ import { defineDesignSystem } from "@agent-native/toolkit/design-system";
2
+
3
+ export const designSystem = defineDesignSystem({});
@@ -0,0 +1,19 @@
1
+ // Import only the tiny URL-helper module, not the full client barrel.
2
+ // This keeps the ~650-700 KB gzip chat stack off the static import closure
3
+ // of the client entry point so it can't block the first page parse.
4
+ import { appBasePath } from "@agent-native/core/client/api-path";
5
+ import { hydrateRoot } from "react-dom/client";
6
+ import { HydratedRouter } from "react-router/dom";
7
+
8
+ const basePath = appBasePath();
9
+ const pathname = window.location.pathname;
10
+ const routerBasePath =
11
+ basePath && (pathname === basePath || pathname.startsWith(`${basePath}/`))
12
+ ? basePath
13
+ : "";
14
+ const context = (
15
+ window as Window & { __reactRouterContext?: { basename?: string } }
16
+ ).__reactRouterContext;
17
+ if (context) context.basename = routerBasePath;
18
+
19
+ hydrateRoot(document, <HydratedRouter />);
@@ -0,0 +1,10 @@
1
+ import {
2
+ createDocumentRequestHandler,
3
+ streamTimeout,
4
+ } from "@agent-native/core/server/entry-server";
5
+ import { ServerRouter } from "react-router";
6
+
7
+ const handleDocumentRequest = createDocumentRequestHandler(ServerRouter);
8
+
9
+ export { streamTimeout };
10
+ export default handleDocumentRequest;
@@ -0,0 +1,93 @@
1
+ /* Self-hosted via @fontsource-variable/inter — no render-blocking Google Fonts request */
2
+ @import "@fontsource-variable/inter";
3
+
4
+ @import "tailwindcss";
5
+ @import "@agent-native/core/styles/agent-native.css";
6
+ @import "@agent-native/toolkit/styles.css";
7
+
8
+ @source "./**/*.{ts,tsx}";
9
+
10
+ :root {
11
+ --background: 0 0% 100%;
12
+ --foreground: 0 0% 10%;
13
+ --card: 0 0% 100%;
14
+ --card-foreground: 0 0% 10%;
15
+ --popover: 0 0% 100%;
16
+ --popover-foreground: 0 0% 10%;
17
+ --primary: 0 0% 15%;
18
+ --primary-foreground: 0 0% 100%;
19
+ --secondary: 0 0% 95%;
20
+ --secondary-foreground: 0 0% 15%;
21
+ --muted: 0 0% 95%;
22
+ --muted-foreground: 0 0% 45%;
23
+ --accent: 0 0% 95%;
24
+ --accent-foreground: 0 0% 15%;
25
+ --destructive: 0 84% 60%;
26
+ --destructive-foreground: 0 0% 98%;
27
+ --border: 0 0% 90%;
28
+ --input: 0 0% 90%;
29
+ --ring: 0 0% 40%;
30
+ --radius: 0.5rem;
31
+ --sidebar-background: 0 0% 97%;
32
+ --sidebar-foreground: 0 0% 45%;
33
+ --sidebar-primary: 0 0% 15%;
34
+ --sidebar-primary-foreground: 0 0% 100%;
35
+ --sidebar-accent: 0 0% 95%;
36
+ --sidebar-accent-foreground: 0 0% 15%;
37
+ --sidebar-border: 0 0% 90%;
38
+ --sidebar-ring: 0 0% 40%;
39
+ }
40
+
41
+ .dark {
42
+ --background: 0 0% 13%;
43
+ --foreground: 0 0% 90%;
44
+ --card: 0 0% 15%;
45
+ --card-foreground: 0 0% 90%;
46
+ --popover: 0 0% 15%;
47
+ --popover-foreground: 0 0% 90%;
48
+ --primary: 0 0% 75%;
49
+ --primary-foreground: 0 0% 10%;
50
+ --secondary: 0 0% 18%;
51
+ --secondary-foreground: 0 0% 90%;
52
+ --muted: 0 0% 16%;
53
+ --muted-foreground: 0 0% 60%;
54
+ --accent: 0 0% 18%;
55
+ --accent-foreground: 0 0% 90%;
56
+ --destructive: 0 84% 60%;
57
+ --destructive-foreground: 0 0% 98%;
58
+ --border: 0 0% 24%;
59
+ --input: 0 0% 24%;
60
+ --ring: 0 0% 60%;
61
+ --radius: 0.5rem;
62
+ --sidebar-background: 0 0% 10%;
63
+ --sidebar-foreground: 0 0% 60%;
64
+ --sidebar-primary: 0 0% 75%;
65
+ --sidebar-primary-foreground: 0 0% 10%;
66
+ --sidebar-accent: 0 0% 16%;
67
+ --sidebar-accent-foreground: 0 0% 90%;
68
+ --sidebar-border: 0 0% 20%;
69
+ --sidebar-ring: 0 0% 60%;
70
+ }
71
+
72
+ @layer base {
73
+ body {
74
+ @apply bg-background text-foreground;
75
+ font-family: "Inter Variable", "Inter", sans-serif;
76
+ font-feature-settings: "cv02", "cv03", "cv04", "cv11";
77
+ }
78
+ }
79
+
80
+ ::-webkit-scrollbar {
81
+ width: 5px;
82
+ height: 5px;
83
+ }
84
+ ::-webkit-scrollbar-track {
85
+ background: transparent;
86
+ }
87
+ ::-webkit-scrollbar-thumb {
88
+ background: hsl(var(--border));
89
+ border-radius: 3px;
90
+ }
91
+ ::-webkit-scrollbar-thumb:hover {
92
+ background: hsl(var(--muted-foreground) / 0.4);
93
+ }