@agent-native/core 0.146.4 → 0.146.6

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 (113) hide show
  1. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +1 -1
  2. package/corpus/templates/analytics/app/components/layout/Layout.tsx +1 -1
  3. package/corpus/templates/analytics/app/pages/Settings.tsx +5 -1
  4. package/corpus/templates/analytics/app/routes/agent.tsx +7 -5
  5. package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +13 -9
  6. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +69 -33
  7. package/corpus/templates/assets/app/components/layout/Layout.tsx +1 -1
  8. package/corpus/templates/assets/app/routes/agent.tsx +7 -5
  9. package/corpus/templates/assets/app/routes/settings.tsx +7 -2
  10. package/corpus/templates/assets/server/plugins/creative-context.ts +1 -1
  11. package/corpus/templates/brain/app/components/layout/Layout.tsx +1 -1
  12. package/corpus/templates/brain/app/lib/brain.ts +4 -2
  13. package/corpus/templates/brain/app/root.tsx +1 -1
  14. package/corpus/templates/brain/app/routes/agent.tsx +10 -2
  15. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +1 -1
  16. package/corpus/templates/calendar/app/root.tsx +1 -2
  17. package/corpus/templates/calendar/app/routes/_app.agent.tsx +8 -16
  18. package/corpus/templates/chat/app/hooks/use-navigation-state.ts +5 -2
  19. package/corpus/templates/chat/app/routes/agent.tsx +7 -31
  20. package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
  21. package/corpus/templates/clips/app/root.tsx +1 -2
  22. package/corpus/templates/clips/app/routes/_app.agent.tsx +9 -7
  23. package/corpus/templates/content/app/components/layout/Layout.tsx +1 -1
  24. package/corpus/templates/content/app/root.tsx +1 -2
  25. package/corpus/templates/content/app/routes/_app.agent.tsx +9 -8
  26. package/corpus/templates/content/app/routes/_app.settings.tsx +7 -2
  27. package/corpus/templates/content/server/plugins/creative-context.ts +1 -1
  28. package/corpus/templates/crm/app/components/crm/RecordActions.tsx +4 -1
  29. package/corpus/templates/crm/app/components/layout/CrmLayout.tsx +1 -1
  30. package/corpus/templates/crm/app/routes/agent.tsx +10 -2
  31. package/corpus/templates/design/app/components/layout/Layout.tsx +1 -1
  32. package/corpus/templates/design/app/root.tsx +1 -1
  33. package/corpus/templates/design/app/routes/agent.tsx +9 -7
  34. package/corpus/templates/design/app/routes/settings.tsx +7 -2
  35. package/corpus/templates/design/server/plugins/creative-context.ts +1 -1
  36. package/corpus/templates/dispatch/app/root.tsx +5 -2
  37. package/corpus/templates/dispatch/app/routes/agent.tsx +7 -7
  38. package/corpus/templates/factory/app/components/layout/Layout.tsx +1 -1
  39. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +5 -2
  40. package/corpus/templates/factory/app/root.tsx +1 -1
  41. package/corpus/templates/factory/app/routes/agent.tsx +9 -14
  42. package/corpus/templates/forms/app/components/layout/Layout.tsx +1 -1
  43. package/corpus/templates/forms/app/root.tsx +1 -1
  44. package/corpus/templates/forms/app/routes/_app.agent.tsx +9 -7
  45. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +5 -3
  46. package/corpus/templates/macros/app/root.tsx +1 -1
  47. package/corpus/templates/macros/app/routes/agent.tsx +9 -6
  48. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +8 -2
  49. package/corpus/templates/mail/app/components/layout/CommandPalette.tsx +1 -1
  50. package/corpus/templates/mail/app/routes/agent.tsx +9 -6
  51. package/corpus/templates/plan/app/components/layout/Layout.tsx +1 -1
  52. package/corpus/templates/plan/app/root.tsx +1 -1
  53. package/corpus/templates/plan/app/routes/agent.tsx +9 -6
  54. package/corpus/templates/slides/app/components/editor/ImageGenPanel.tsx +8 -12
  55. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +39 -2
  56. package/corpus/templates/slides/app/components/layout/Layout.tsx +1 -1
  57. package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
  58. package/corpus/templates/slides/app/lib/create-deck-generation.ts +22 -5
  59. package/corpus/templates/slides/app/pages/Index.tsx +20 -5
  60. package/corpus/templates/slides/app/root.tsx +1 -1
  61. package/corpus/templates/slides/app/routes/agent.tsx +6 -9
  62. package/corpus/templates/slides/app/routes/settings.tsx +7 -2
  63. package/corpus/templates/slides/server/plugins/creative-context.ts +1 -1
  64. package/corpus/templates/tasks/app/components/layout/Layout.tsx +1 -1
  65. package/corpus/templates/tasks/app/routes/agent.tsx +10 -2
  66. package/dist/client/index.d.ts +1 -1
  67. package/dist/client/navigation/index.d.ts +1 -1
  68. package/dist/client/navigation/index.js +1 -1
  69. package/dist/client/org/OrgSwitcher.js +1 -1
  70. package/dist/client/settings/SettingsPanel.d.ts +8 -0
  71. package/dist/client/settings/SettingsPanel.js +16 -1
  72. package/dist/client/settings/index.d.ts +1 -1
  73. package/dist/collab/awareness.d.ts +2 -2
  74. package/dist/collab/routes.d.ts +1 -1
  75. package/dist/deploy/build.d.ts +21 -0
  76. package/dist/deploy/build.js +185 -10
  77. package/dist/jobs/background-automation-runner.d.ts +1 -1
  78. package/dist/jobs/background-automation-runner.js +1 -1
  79. package/dist/jobs/scheduler-health.d.ts +4 -0
  80. package/dist/jobs/scheduler-health.js +33 -0
  81. package/dist/localization/server.d.ts +0 -1
  82. package/dist/localization/server.js +3 -1
  83. package/dist/mcp-client/index.d.ts +1 -1
  84. package/dist/mcp-client/index.js +1 -1
  85. package/dist/mcp-client/routes.d.ts +11 -0
  86. package/dist/mcp-client/routes.js +26 -1
  87. package/dist/navigation/index.d.ts +1 -0
  88. package/dist/navigation/index.js +34 -0
  89. package/dist/notifications/routes.d.ts +2 -2
  90. package/dist/observability/routes.d.ts +3 -3
  91. package/dist/provider-api/actions/custom-provider-registration.d.ts +6 -6
  92. package/dist/provider-api/actions/provider-api.d.ts +5 -5
  93. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  94. package/dist/secrets/routes.d.ts +6 -6
  95. package/dist/server/agent-chat/mcp-glue.js +12 -3
  96. package/dist/server/agent-chat-plugin.js +73 -48
  97. package/dist/server/http-response-telemetry.js +112 -5
  98. package/dist/server/realtime-token.d.ts +1 -1
  99. package/dist/server/release-migrations.js +2 -0
  100. package/dist/server/ssr-handler.js +8 -0
  101. package/dist/server/transcribe-voice.d.ts +1 -1
  102. package/dist/templates/chat/app/hooks/use-navigation-state.ts +5 -2
  103. package/dist/templates/chat/app/routes/agent.tsx +7 -31
  104. package/dist/templates/default/app/routes/_index.tsx +1 -1
  105. package/dist/templates/default/app/routes/agent.tsx +7 -25
  106. package/dist/templates/workspace-core/.agents/skills/performance/SKILL.md +54 -3
  107. package/dist/vite/client.js +5 -1
  108. package/package.json +1 -1
  109. package/src/templates/chat/app/hooks/use-navigation-state.ts +5 -2
  110. package/src/templates/chat/app/routes/agent.tsx +7 -31
  111. package/src/templates/default/app/routes/_index.tsx +1 -1
  112. package/src/templates/default/app/routes/agent.tsx +7 -25
  113. package/src/templates/workspace-core/.agents/skills/performance/SKILL.md +54 -3
@@ -508,7 +508,7 @@ export function CommandPalette() {
508
508
  <CommandGroup key="settings" heading={t("navigation.settings")}>
509
509
  <CommandItem
510
510
  value={`setting:agent-page:${t("settings.agentTitle")}`}
511
- onSelect={() => go("/agent")}
511
+ onSelect={() => go("/settings/agent")}
512
512
  keywords={commandPaletteKeywords(
513
513
  t("settings.agentTitle"),
514
514
  "agent",
@@ -205,7 +205,7 @@ function InteractiveLayout({ children }: LayoutProps) {
205
205
  openOnChatRunning={chatHomeHandoffActive}
206
206
  onFullscreenRequest={openAskAgentFullscreen}
207
207
  emptyStateText={t("chat.emptyState")}
208
- agentPageHref="/agent"
208
+ agentPageHref="/settings/agent"
209
209
  suggestions={[
210
210
  t("chat.suggestionArrGrowth"),
211
211
  t("chat.suggestionChurn"),
@@ -13,6 +13,7 @@ import {
13
13
  useAgentSettingsTabs,
14
14
  type SettingsTabItem,
15
15
  } from "@agent-native/core/client/settings";
16
+ import { createCreativeContextAgentTab } from "@agent-native/creative-context/client";
16
17
  import { IconBell } from "@tabler/icons-react";
17
18
  import { useEffect, useMemo, useState } from "react";
18
19
  import { Link } from "react-router";
@@ -136,7 +137,10 @@ export default function Settings() {
136
137
  }
137
138
  />
138
139
  );
139
- const agentSettingsTabs = useAgentSettingsTabs({ agentAdditionalContent });
140
+ const agentSettingsTabs = useAgentSettingsTabs({
141
+ agentAdditionalContent,
142
+ agentAdditionalTabFactories: [createCreativeContextAgentTab],
143
+ });
140
144
 
141
145
  const extraTabs = useMemo<SettingsTabItem[]>(
142
146
  () => [
@@ -1,11 +1,13 @@
1
- import { AgentTabsPage } from "@agent-native/core/client/agent-chat";
2
- import { createCreativeContextAgentTab } from "@agent-native/creative-context/client";
1
+ import { buildLegacyAgentSettingsRoute } from "@agent-native/core/client/navigation";
2
+ import { Navigate, useLocation } from "react-router";
3
3
 
4
4
  export default function AgentRoute() {
5
+ const location = useLocation();
6
+
5
7
  return (
6
- <AgentTabsPage
7
- appName="Analytics"
8
- extraTabFactories={[createCreativeContextAgentTab]}
8
+ <Navigate
9
+ to={buildLegacyAgentSettingsRoute(location.hash, location.search)}
10
+ replace
9
11
  />
10
12
  );
11
13
  }
@@ -201,21 +201,13 @@ export async function queueFirstPartyAnalyticsBigQueryBackfill(
201
201
  const id = jobId(scope.orgId ?? "");
202
202
  if (!scope.orgId)
203
203
  throw new Error("BigQuery backfill requires an organization");
204
- const conflictClause =
205
- requestedBatchSize === undefined
206
- ? "ON CONFLICT (id) DO NOTHING"
207
- : `ON CONFLICT (id) DO UPDATE SET batch_size = CASE
208
- WHEN batch_size < excluded.batch_size
209
- THEN excluded.batch_size
210
- ELSE batch_size
211
- END`;
212
204
  await executor().execute({
213
205
  sql: `INSERT INTO ${JOB_TABLE} (
214
206
  id, org_id, owner_email, table_ref, batch_size, backfill_cursor,
215
207
  status, copied_count, lease_token, lease_expires_at, next_run_at,
216
208
  last_error, completed_at, updated_at
217
209
  ) VALUES (?, ?, ?, ?, ?, ?, 'pending', 0, NULL, NULL, ?, NULL, NULL, ?)
218
- ${conflictClause}`,
210
+ ON CONFLICT (id) DO NOTHING`,
219
211
  args: [
220
212
  id,
221
213
  scope.orgId,
@@ -229,6 +221,18 @@ export async function queueFirstPartyAnalyticsBigQueryBackfill(
229
221
  timeoutMs: 5_000,
230
222
  maxAttempts: 1,
231
223
  });
224
+ if (requestedBatchSize !== undefined) {
225
+ const batchSize = boundedBatchSize(requestedBatchSize);
226
+ await executor().execute({
227
+ sql: `UPDATE ${JOB_TABLE}
228
+ SET batch_size = ?
229
+ WHERE id = ?
230
+ AND batch_size < ?`,
231
+ args: [batchSize, id, batchSize],
232
+ timeoutMs: 5_000,
233
+ maxAttempts: 1,
234
+ });
235
+ }
232
236
  const job = await getFirstPartyAnalyticsBigQueryBackfillJob(scope);
233
237
  if (!job) throw new Error("BigQuery backfill job was not persisted");
234
238
  if (job.table !== table) {
@@ -1031,7 +1031,7 @@ function backfillBranchSql(
1031
1031
  ? [cursor.receivedAt, cursor.receivedAt, cursor.id]
1032
1032
  : [];
1033
1033
  return {
1034
- sql: `SELECT ${FIRST_PARTY_ANALYTICS_BACKFILL_COLUMNS.join(", ")}
1034
+ sql: `SELECT id, received_at
1035
1035
  FROM analytics_events
1036
1036
  WHERE ${predicate}${cursorSql ? ` AND ${cursorSql}` : ""}
1037
1037
  ORDER BY received_at ASC, id ASC LIMIT ?`,
@@ -1039,32 +1039,20 @@ function backfillBranchSql(
1039
1039
  };
1040
1040
  }
1041
1041
 
1042
- function backfillScopedRowsSql(
1043
- branches: Array<{ predicate: string; args: string[] }>,
1044
- cursor: FirstPartyAnalyticsBackfillCursor,
1045
- limit: number,
1046
- ): { sql: string; args: Array<string | number> } {
1047
- const branchQueries = branches.map((branch) =>
1048
- backfillBranchSql(branch.predicate, branch.args, cursor),
1049
- );
1050
- const columns = FIRST_PARTY_ANALYTICS_BACKFILL_COLUMNS.join(", ");
1042
+ function backfillRowsByIdsSql(ids: string[]): {
1043
+ sql: string;
1044
+ args: string[];
1045
+ } {
1051
1046
  return {
1052
- sql: `WITH ${branchQueries
1053
- .map(
1054
- (_branch, index) =>
1055
- `backfill_branch_${index} AS (${branchQueries[index]!.sql})`,
1056
- )
1057
- .join(", ")}
1058
- SELECT ${columns}
1059
- FROM (${branchQueries.map((_branch, index) => `SELECT ${columns} FROM backfill_branch_${index}`).join(" UNION ALL ")}) AS selected_events
1060
- ORDER BY received_at ASC, id ASC LIMIT ?`,
1061
- args: [
1062
- ...branchQueries.flatMap((branch) => [...branch.args, limit]),
1063
- limit,
1064
- ],
1047
+ sql: `SELECT ${FIRST_PARTY_ANALYTICS_BACKFILL_COLUMNS.join(", ")}
1048
+ FROM analytics_events
1049
+ WHERE id IN (${ids.map(() => "?").join(", ")})`,
1050
+ args: ids,
1065
1051
  };
1066
1052
  }
1067
1053
 
1054
+ const MAX_SQLITE_BIND_VARIABLES = 900;
1055
+
1068
1056
  function backfillRowCursor(
1069
1057
  row: Record<string, unknown>,
1070
1058
  ): FirstPartyAnalyticsBackfillCursor {
@@ -1081,6 +1069,18 @@ function backfillRowCursor(
1081
1069
  return { receivedAt, id };
1082
1070
  }
1083
1071
 
1072
+ function compareBackfillRows(
1073
+ left: Record<string, unknown>,
1074
+ right: Record<string, unknown>,
1075
+ ): number {
1076
+ const leftCursor = backfillRowCursor(left);
1077
+ const rightCursor = backfillRowCursor(right);
1078
+ return (
1079
+ leftCursor.receivedAt.localeCompare(rightCursor.receivedAt) ||
1080
+ leftCursor.id.localeCompare(rightCursor.id)
1081
+ );
1082
+ }
1083
+
1084
1084
  export async function backfillFirstPartyAnalyticsBatch(
1085
1085
  scope: FirstPartyAnalyticsScope,
1086
1086
  cursor: string | null,
@@ -1108,15 +1108,22 @@ export async function backfillFirstPartyAnalyticsBatch(
1108
1108
  },
1109
1109
  ];
1110
1110
  const rows: Record<string, unknown>[] = [];
1111
- const scoped = backfillScopedRowsSql(branches, parsedCursor, boundedLimit);
1112
- const result = await db.execute({
1113
- sql: scoped.sql,
1114
- args: scoped.args,
1115
- timeoutMs: 20_000,
1116
- maxAttempts: 1,
1117
- });
1118
- rows.push(...(result.rows as Record<string, unknown>[]));
1119
- const selectedRows = rows;
1111
+ for (const branch of branches) {
1112
+ const scoped = backfillBranchSql(
1113
+ branch.predicate,
1114
+ branch.args,
1115
+ parsedCursor,
1116
+ );
1117
+ const result = await db.execute({
1118
+ sql: scoped.sql,
1119
+ args: [...scoped.args, boundedLimit],
1120
+ timeoutMs: 20_000,
1121
+ maxAttempts: 1,
1122
+ });
1123
+ rows.push(...(result.rows as Record<string, unknown>[]));
1124
+ }
1125
+ rows.sort(compareBackfillRows);
1126
+ const selectedRows = rows.slice(0, boundedLimit);
1120
1127
  if (!selectedRows.length) {
1121
1128
  return {
1122
1129
  nextCursor: serializeBackfillCursor(parsedCursor),
@@ -1125,7 +1132,36 @@ export async function backfillFirstPartyAnalyticsBatch(
1125
1132
  };
1126
1133
  }
1127
1134
 
1128
- const selectedEvents = selectedRows;
1135
+ const selectedIds = selectedRows.map((row) => backfillRowCursor(row).id);
1136
+ const hydratedRows: Record<string, unknown>[] = [];
1137
+ for (
1138
+ let offset = 0;
1139
+ offset < selectedIds.length;
1140
+ offset += MAX_SQLITE_BIND_VARIABLES
1141
+ ) {
1142
+ const hydratedQuery = backfillRowsByIdsSql(
1143
+ selectedIds.slice(offset, offset + MAX_SQLITE_BIND_VARIABLES),
1144
+ );
1145
+ const hydratedResult = await db.execute({
1146
+ sql: hydratedQuery.sql,
1147
+ args: hydratedQuery.args,
1148
+ timeoutMs: 20_000,
1149
+ maxAttempts: 1,
1150
+ });
1151
+ hydratedRows.push(...(hydratedResult.rows as Record<string, unknown>[]));
1152
+ }
1153
+ const hydratedById = new Map(
1154
+ hydratedRows.map((row) => [backfillRowCursor(row).id, row]),
1155
+ );
1156
+ const selectedEvents = selectedIds.map((id) => {
1157
+ const row = hydratedById.get(id);
1158
+ if (!row) {
1159
+ throw new Error(
1160
+ `First-party analytics backfill row ${id} disappeared before hydration`,
1161
+ );
1162
+ }
1163
+ return row;
1164
+ });
1129
1165
  await insertFirstPartyAnalyticsRows(selectedEvents, configuredTable);
1130
1166
  const lastCursor = backfillRowCursor(selectedRows[selectedRows.length - 1]!);
1131
1167
  return {
@@ -165,7 +165,7 @@ export function Layout({ children }: LayoutProps) {
165
165
  openOnChatRunning={chatHomeHandoffActive}
166
166
  onFullscreenRequest={openCreateChatFullscreen}
167
167
  emptyStateText={t("chat.emptyState")}
168
- agentPageHref="/agent"
168
+ agentPageHref="/settings/agent"
169
169
  suggestions={[
170
170
  t("chat.suggestionBlogHeroes"),
171
171
  t("chat.suggestionProductVideo"),
@@ -1,11 +1,13 @@
1
- import { AgentTabsPage } from "@agent-native/core/client/agent-chat";
2
- import { createCreativeContextAgentTab } from "@agent-native/creative-context/client";
1
+ import { buildLegacyAgentSettingsRoute } from "@agent-native/core/client/navigation";
2
+ import { Navigate, useLocation } from "react-router";
3
3
 
4
4
  export default function AgentRoute() {
5
+ const location = useLocation();
6
+
5
7
  return (
6
- <AgentTabsPage
7
- appName="Assets"
8
- extraTabFactories={[createCreativeContextAgentTab]}
8
+ <Navigate
9
+ to={buildLegacyAgentSettingsRoute(location.hash, location.search)}
10
+ replace
9
11
  />
10
12
  );
11
13
  }
@@ -18,7 +18,10 @@ import {
18
18
  useBuilderStatus,
19
19
  type SettingsSearchEntry,
20
20
  } from "@agent-native/core/client/settings";
21
- import { CreativeContextSettingsLink } from "@agent-native/creative-context/client";
21
+ import {
22
+ CreativeContextSettingsLink,
23
+ createCreativeContextAgentTab,
24
+ } from "@agent-native/creative-context/client";
22
25
  import {
23
26
  IconAlertCircle,
24
27
  IconCheck,
@@ -87,7 +90,9 @@ type FormOnboardingMethod = Extract<OnboardingMethod, { kind: "form" }>;
87
90
 
88
91
  export default function SettingsPage() {
89
92
  const t = useT();
90
- const agentSettingsTabs = useAgentSettingsTabs();
93
+ const agentSettingsTabs = useAgentSettingsTabs({
94
+ agentAdditionalTabFactories: [createCreativeContextAgentTab],
95
+ });
91
96
  const { data } = useActionQuery("list-libraries", { compact: true }) as {
92
97
  data?: { count?: number };
93
98
  };
@@ -191,7 +191,7 @@ registerOnboardingStep({
191
191
  kind: "link",
192
192
  primary: true,
193
193
  label: "Open Library",
194
- payload: { url: "/agent#library", external: false },
194
+ payload: { url: "/settings/library", external: false },
195
195
  },
196
196
  ],
197
197
  isComplete: async () => {
@@ -137,7 +137,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
137
137
  openOnChatRunning={chatHomeHandoffActive}
138
138
  onFullscreenRequest={openAskAgentFullscreen}
139
139
  emptyStateText={t("chat.emptyState")}
140
- agentPageHref="/agent"
140
+ agentPageHref="/settings/agent"
141
141
  suggestions={[
142
142
  t("chat.suggestionSecurity"),
143
143
  t("chat.suggestionStaleFacts"),
@@ -1050,7 +1050,9 @@ export function viewFromPath(pathname: string): BrainView {
1050
1050
  if (pathname.startsWith("/review")) return "review";
1051
1051
  if (pathname.startsWith("/sources")) return "sources";
1052
1052
  if (pathname.startsWith("/ops")) return "ops";
1053
- if (pathname.startsWith("/agent")) return "agent";
1053
+ if (pathname.startsWith("/settings/agent") || pathname.startsWith("/agent")) {
1054
+ return "agent";
1055
+ }
1054
1056
  if (pathname.startsWith("/settings")) return "settings";
1055
1057
  return "ask";
1056
1058
  }
@@ -1070,7 +1072,7 @@ export function pathFromView(view?: string): string {
1070
1072
  case "ops":
1071
1073
  return "/ops";
1072
1074
  case "agent":
1073
- return "/agent";
1075
+ return "/settings/agent";
1074
1076
  case "settings":
1075
1077
  return "/settings";
1076
1078
  case "ask":
@@ -183,7 +183,7 @@ function AppContent() {
183
183
  {t("navigation.settings")}
184
184
  </CommandMenu.Item>
185
185
  <CommandMenu.Item
186
- onSelect={() => navigate("/agent")}
186
+ onSelect={() => navigate("/settings/agent")}
187
187
  keywords={[
188
188
  "agent",
189
189
  "context",
@@ -1,4 +1,5 @@
1
- import { AgentTabsPage } from "@agent-native/core/client/agent-chat";
1
+ import { buildLegacyAgentSettingsRoute } from "@agent-native/core/client/navigation";
2
+ import { Navigate, useLocation } from "react-router";
2
3
 
3
4
  import { messagesByLocale } from "@/i18n-data";
4
5
 
@@ -7,5 +8,12 @@ export function meta() {
7
8
  }
8
9
 
9
10
  export default function AgentRoute() {
10
- return <AgentTabsPage appName="Brain" />;
11
+ const location = useLocation();
12
+
13
+ return (
14
+ <Navigate
15
+ to={buildLegacyAgentSettingsRoute(location.hash, location.search)}
16
+ replace
17
+ />
18
+ );
11
19
  }
@@ -394,7 +394,7 @@ export function AppLayout({ children }: AppLayoutProps) {
394
394
  position="right"
395
395
  defaultOpen={false}
396
396
  emptyStateText={t("agentSidebar.emptyState")}
397
- agentPageHref="/agent"
397
+ agentPageHref="/settings/agent"
398
398
  suggestions={[
399
399
  t("agentSidebar.suggestions.today"),
400
400
  t("agentSidebar.suggestions.findSlot"),
@@ -95,7 +95,6 @@ export function Layout({ children }: { children: React.ReactNode }) {
95
95
  const localeInitScript = getLocaleInitScript({
96
96
  locale: loaderData.locale,
97
97
  preference: loaderData.preference,
98
- messages: loaderData.messages,
99
98
  });
100
99
 
101
100
  return (
@@ -223,7 +222,7 @@ function AppContent() {
223
222
  {t("root.commandSearch")}
224
223
  </CommandMenu.Item>
225
224
  <CommandMenu.Item
226
- onSelect={() => navigate("/agent")}
225
+ onSelect={() => navigate("/settings/agent")}
227
226
  keywords={[
228
227
  "agent",
229
228
  "context",
@@ -1,8 +1,6 @@
1
- import { AgentTabsPage } from "@agent-native/core/client/agent-chat";
2
- import { useT } from "@agent-native/core/client/i18n";
3
- import { useMemo } from "react";
1
+ import { buildLegacyAgentSettingsRoute } from "@agent-native/core/client/navigation";
2
+ import { Navigate, useLocation } from "react-router";
4
3
 
5
- import { useAppHeaderControls } from "@/components/layout/AppLayout";
6
4
  import { messagesByLocale } from "@/i18n-data";
7
5
 
8
6
  export function meta() {
@@ -10,17 +8,11 @@ export function meta() {
10
8
  }
11
9
 
12
10
  export default function AgentRoute() {
13
- const t = useT();
14
- const controls = useMemo(
15
- () => ({
16
- left: (
17
- <h1 className="truncate text-lg font-semibold tracking-tight">
18
- {t("settings.agentTitle")}
19
- </h1>
20
- ),
21
- }),
22
- [t],
11
+ const location = useLocation();
12
+ return (
13
+ <Navigate
14
+ to={buildLegacyAgentSettingsRoute(location.hash, location.search)}
15
+ replace
16
+ />
23
17
  );
24
- useAppHeaderControls(controls);
25
- return <AgentTabsPage appName="Calendar" />;
26
18
  }
@@ -42,7 +42,10 @@ function viewForPath(pathname: string): string {
42
42
  if (pathname.startsWith("/database")) return "database";
43
43
  if (pathname.startsWith("/extensions")) return "extensions";
44
44
  if (pathname.startsWith("/observability")) return "observability";
45
- if (pathname.startsWith("/agent")) return "agent";
45
+ if (pathname.startsWith("/settings/agent") || pathname.startsWith("/agent")) {
46
+ return "agent";
47
+ }
48
+ if (pathname.startsWith("/settings")) return "settings";
46
49
  if (pathname.startsWith("/team")) return "settings";
47
50
  return "chat";
48
51
  }
@@ -60,7 +63,7 @@ function pathForView(view?: string): string {
60
63
  case "observability":
61
64
  return "/observability";
62
65
  case "agent":
63
- return "/agent";
66
+ return "/settings/agent";
64
67
  case "settings":
65
68
  return "/settings";
66
69
  case "team":
@@ -1,5 +1,4 @@
1
- import { useT } from "@agent-native/core/client/i18n";
2
- import { useSetPageTitle } from "@agent-native/toolkit/app-shell";
1
+ import { buildLegacyAgentSettingsRoute } from "@agent-native/core/client/navigation";
3
2
  import { Navigate, useLocation } from "react-router";
4
3
 
5
4
  export function meta() {
@@ -7,34 +6,11 @@ export function meta() {
7
6
  }
8
7
 
9
8
  export default function AgentRoute() {
10
- const t = useT();
11
9
  const location = useLocation();
12
- useSetPageTitle(t("settings.agentTitle"));
13
-
14
- const legacyTab = location.hash.replace(/^#/, "").toLowerCase();
15
- const resourceTabs = new Set([
16
- "files",
17
- "instructions",
18
- "agents",
19
- "memory",
20
- "skills",
21
- "learnings",
22
- ]);
23
- const destination = resourceTabs.has(legacyTab)
24
- ? `/settings/agent/resources/${legacyTab}`
25
- : legacyTab === "remote-agents"
26
- ? "/settings/agent/agents"
27
- : legacyTab === "connections"
28
- ? "/settings/integrations"
29
- : legacyTab === "jobs"
30
- ? "/settings/agent/automations"
31
- : legacyTab === "access"
32
- ? "/settings/agent"
33
- : ["llm", "app-models", "limits", "voice", "background"].includes(
34
- legacyTab,
35
- )
36
- ? `/settings/agent/${legacyTab}`
37
- : "/settings/agent";
38
-
39
- return <Navigate to={destination} replace />;
10
+ return (
11
+ <Navigate
12
+ to={buildLegacyAgentSettingsRoute(location.hash, location.search)}
13
+ replace
14
+ />
15
+ );
40
16
  }
@@ -722,7 +722,7 @@ export function LibraryLayout({ children }: LibraryLayoutProps) {
722
722
  t("navigation.agentSuggestionPricing"),
723
723
  t("navigation.agentSuggestionFiller"),
724
724
  ]}
725
- agentPageHref="/agent"
725
+ agentPageHref="/settings/agent"
726
726
  scope={recordingScope}
727
727
  browserTabId={getBrowserTabId()}
728
728
  >
@@ -130,7 +130,6 @@ export function Layout({ children }: { children: React.ReactNode }) {
130
130
  const localeInitScript = getHydrationStableLocaleInitScript({
131
131
  locale: loaderData.locale,
132
132
  preference: loaderData.preference,
133
- messages: loaderData.messages,
134
133
  });
135
134
 
136
135
  return (
@@ -369,7 +368,7 @@ function AppContent() {
369
368
  changelogKey="clips"
370
369
  >
371
370
  <CommandMenu.Group heading={t("root.commandActions")}>
372
- <CommandMenu.Item onSelect={() => navigate("/agent")}>
371
+ <CommandMenu.Item onSelect={() => navigate("/settings/agent")}>
373
372
  <IconHierarchy2 size={16} />
374
373
  {t("root.openAgent")}
375
374
  </CommandMenu.Item>
@@ -1,6 +1,5 @@
1
- import { AgentTabsPage } from "@agent-native/core/client/agent-chat";
2
- import { useT } from "@agent-native/core/client/i18n";
3
- import { useSetPageTitle } from "@agent-native/toolkit/app-shell";
1
+ import { buildLegacyAgentSettingsRoute } from "@agent-native/core/client/navigation";
2
+ import { Navigate, useLocation } from "react-router";
4
3
 
5
4
  import messages from "@/i18n/en-US";
6
5
 
@@ -9,8 +8,11 @@ export function meta() {
9
8
  }
10
9
 
11
10
  export default function AgentRoute() {
12
- const t = useT();
13
- useSetPageTitle(t("settings.agentTitle"));
14
-
15
- return <AgentTabsPage />;
11
+ const location = useLocation();
12
+ return (
13
+ <Navigate
14
+ to={buildLegacyAgentSettingsRoute(location.hash, location.search)}
15
+ replace
16
+ />
17
+ );
16
18
  }
@@ -192,7 +192,7 @@ export function Layout({ children }: LayoutProps) {
192
192
  <AgentSidebar
193
193
  position="right"
194
194
  defaultOpen={false}
195
- agentPageHref="/agent"
195
+ agentPageHref="/settings/agent"
196
196
  emptyStateText={t("chat.emptyState")}
197
197
  suggestions={[
198
198
  t("chat.suggestionPrd"),
@@ -188,7 +188,6 @@ export function Layout({ children }: { children: React.ReactNode }) {
188
188
  const localeInitScript = getLocaleInitScript({
189
189
  locale: loaderData.locale,
190
190
  preference: loaderData.preference,
191
- messages: loaderData.messages,
192
191
  });
193
192
 
194
193
  return (
@@ -570,7 +569,7 @@ function ContentCommandMenu({
570
569
  )}
571
570
  >
572
571
  <CommandMenu.Group heading={t("root.commandContent")}>
573
- <CommandMenu.Item onSelect={() => navigate("/agent")}>
572
+ <CommandMenu.Item onSelect={() => navigate("/settings/agent")}>
574
573
  <IconHierarchy2 size={16} />
575
574
  {t("root.openAgent")}
576
575
  </CommandMenu.Item>
@@ -1,7 +1,5 @@
1
- import { AgentTabsPage } from "@agent-native/core/client/agent-chat";
2
- import { useT } from "@agent-native/core/client/i18n";
3
- import { createCreativeContextAgentTab } from "@agent-native/creative-context/client";
4
- import { useSetPageTitle } from "@agent-native/toolkit/app-shell";
1
+ import { buildLegacyAgentSettingsRoute } from "@agent-native/core/client/navigation";
2
+ import { Navigate, useLocation } from "react-router";
5
3
 
6
4
  import { messagesByLocale } from "@/i18n-data";
7
5
 
@@ -10,8 +8,11 @@ export function meta() {
10
8
  }
11
9
 
12
10
  export default function AgentRoute() {
13
- const t = useT();
14
- useSetPageTitle(t("settings.agentTitle"));
15
-
16
- return <AgentTabsPage extraTabFactories={[createCreativeContextAgentTab]} />;
11
+ const location = useLocation();
12
+ return (
13
+ <Navigate
14
+ to={buildLegacyAgentSettingsRoute(location.hash, location.search)}
15
+ replace
16
+ />
17
+ );
17
18
  }
@@ -9,7 +9,10 @@ import {
9
9
  useAgentSettingsTabs,
10
10
  type SettingsSearchEntry,
11
11
  } from "@agent-native/core/client/settings";
12
- import { CreativeContextSettingsLink } from "@agent-native/creative-context/client";
12
+ import {
13
+ CreativeContextSettingsLink,
14
+ createCreativeContextAgentTab,
15
+ } from "@agent-native/creative-context/client";
13
16
  import { useSetPageTitle } from "@agent-native/toolkit/app-shell";
14
17
  import { useMemo } from "react";
15
18
  import { toast } from "sonner";
@@ -26,7 +29,9 @@ export function meta() {
26
29
 
27
30
  export default function SettingsRoute() {
28
31
  const t = useT();
29
- const agentSettingsTabs = useAgentSettingsTabs();
32
+ const agentSettingsTabs = useAgentSettingsTabs({
33
+ agentAdditionalTabFactories: [createCreativeContextAgentTab],
34
+ });
30
35
  useSetPageTitle(t("settings.title"));
31
36
  const { prefs, loading: prefsLoading, save: savePrefs } = useContentPrefs();
32
37
 
@@ -20,7 +20,7 @@ registerOnboardingStep({
20
20
  kind: "link",
21
21
  primary: true,
22
22
  label: "Open Library",
23
- payload: { url: "/agent#library", external: false },
23
+ payload: { url: "/settings/library", external: false },
24
24
  },
25
25
  ],
26
26
  isComplete: async () => {