@agent-native/core 0.120.1 → 0.120.2

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 (170) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +9 -0
  3. package/corpus/core/docs/content/extensions.mdx +23 -4
  4. package/corpus/core/package.json +1 -1
  5. package/corpus/core/src/agent/production-agent.ts +4 -0
  6. package/corpus/core/src/agent/run-manager.ts +36 -10
  7. package/corpus/core/src/agent/types.ts +2 -0
  8. package/corpus/core/src/client/extensions/ExtensionsListPage.tsx +16 -3
  9. package/corpus/core/src/client/settings/ExtensionsSettingsContent.tsx +24 -0
  10. package/corpus/core/src/client/settings/SettingsPanel.tsx +10 -0
  11. package/corpus/core/src/deploy/build.ts +3 -0
  12. package/corpus/core/src/server/analytics.ts +154 -25
  13. package/corpus/core/src/templates/chat/app/components/layout/Sidebar.tsx +35 -6
  14. package/corpus/core/src/templates/chat/app/routes/extensions._index.tsx +2 -2
  15. package/corpus/core/src/templates/chat/netlify.toml +1 -0
  16. package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
  17. package/corpus/core/src/vite/client.ts +6 -0
  18. package/corpus/templates/analytics/.agents/skills/adhoc-analysis/SKILL.md +27 -43
  19. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +19 -8
  20. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +2 -2
  21. package/corpus/templates/analytics/AGENTS.md +20 -8
  22. package/corpus/templates/analytics/actions/get-explorer-dashboard.ts +3 -0
  23. package/corpus/templates/analytics/actions/list-dashboard-usage-stats.ts +140 -46
  24. package/corpus/templates/analytics/actions/migrate-analytics-artifacts.ts +48 -0
  25. package/corpus/templates/analytics/actions/save-analysis.ts +2 -3
  26. package/corpus/templates/analytics/actions/view-screen.ts +1 -1
  27. package/corpus/templates/analytics/app/components/dashboard/DashboardMetadata.tsx +77 -0
  28. package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +7 -1
  29. package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +1 -0
  30. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +103 -502
  31. package/corpus/templates/analytics/app/global.css +30 -0
  32. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +2 -2
  33. package/corpus/templates/analytics/app/i18n-data.ts +80 -0
  34. package/corpus/templates/analytics/app/pages/Agents.tsx +49 -2
  35. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +54 -11
  36. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +3 -53
  37. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +23 -25
  38. package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +2 -2
  39. package/corpus/templates/analytics/app/routes/analyses._index.tsx +3 -2
  40. package/corpus/templates/analytics/app/routes/extensions._index.tsx +2 -2
  41. package/corpus/templates/analytics/changelog/2026-07-23-analytics-now-groups-saved-analyses-and-embedded-extensions-.md +6 -0
  42. package/corpus/templates/analytics/changelog/2026-07-23-dashboard-menus-show-who-created-and-last-updated-each-dashb.md +6 -0
  43. package/corpus/templates/analytics/changelog/2026-07-23-dashboard-usage-now-loads-reliably-for-organizations-with-ma.md +6 -0
  44. package/corpus/templates/analytics/changelog/2026-07-23-long-dashboard-building-requests-now-continue-without-stoppi.md +6 -0
  45. package/corpus/templates/analytics/netlify.toml +4 -0
  46. package/corpus/templates/analytics/server/db/schema.ts +4 -0
  47. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +0 -1
  48. package/corpus/templates/analytics/server/lib/agent-readable-resource-context.ts +1 -0
  49. package/corpus/templates/analytics/server/lib/migrate-analytics-artifacts.ts +1062 -0
  50. package/corpus/templates/analytics/server/plugins/agent-chat.ts +6 -52
  51. package/corpus/templates/analytics/server/plugins/core-routes.ts +1 -1
  52. package/corpus/templates/analytics/server/plugins/db.ts +10 -0
  53. package/corpus/templates/assets/actions/generate-image-batch.ts +36 -1
  54. package/corpus/templates/assets/actions/generate-image.ts +28 -1
  55. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +46 -8
  56. package/corpus/templates/assets/app/routes/extensions._index.tsx +2 -2
  57. package/corpus/templates/assets/changelog/2026-07-23-assets-image-generation-now-recovers-after-a-completed-tool-.md +6 -0
  58. package/corpus/templates/assets/netlify.toml +1 -0
  59. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +38 -8
  60. package/corpus/templates/brain/app/routes/extensions._index.tsx +2 -2
  61. package/corpus/templates/brain/netlify.toml +1 -0
  62. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +43 -5
  63. package/corpus/templates/calendar/app/routes/_app.extensions._index.tsx +2 -16
  64. package/corpus/templates/calendar/netlify.toml +1 -0
  65. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +35 -6
  66. package/corpus/templates/chat/app/routes/extensions._index.tsx +2 -2
  67. package/corpus/templates/chat/netlify.toml +1 -0
  68. package/corpus/templates/clips/app/components/library/library-layout.tsx +52 -6
  69. package/corpus/templates/clips/app/routes/_app.extensions._index.tsx +2 -16
  70. package/corpus/templates/clips/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
  71. package/corpus/templates/clips/changelog/2026-07-23-meeting-recording-meters-and-dismissed-prompts.md +6 -0
  72. package/corpus/templates/clips/desktop/src/hooks/useMicMeter.ts +1 -1
  73. package/corpus/templates/clips/desktop/src/lib/meeting-notification-dismissal.ts +2 -0
  74. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +1 -3
  75. package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +86 -0
  76. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +24 -0
  77. package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +5 -69
  78. package/corpus/templates/clips/desktop/src/styles.css +35 -3
  79. package/corpus/templates/clips/desktop/src-tauri/src/adhoc_meetings_watcher.rs +13 -44
  80. package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +119 -17
  81. package/corpus/templates/clips/netlify.toml +1 -0
  82. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +3 -7
  83. package/corpus/templates/content/app/routes/_app.extensions._index.tsx +2 -2
  84. package/corpus/templates/content/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
  85. package/corpus/templates/content/netlify.toml +1 -0
  86. package/corpus/templates/design/app/components/layout/Sidebar.tsx +55 -17
  87. package/corpus/templates/design/app/routes/extensions._index.tsx +2 -2
  88. package/corpus/templates/design/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
  89. package/corpus/templates/design/netlify.toml +1 -0
  90. package/corpus/templates/dispatch/app/dispatch-extensions.tsx +1 -1
  91. package/corpus/templates/dispatch/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
  92. package/corpus/templates/dispatch/netlify.toml +1 -0
  93. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +41 -45
  94. package/corpus/templates/forms/app/routes/_app.extensions._index.tsx +2 -2
  95. package/corpus/templates/forms/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
  96. package/corpus/templates/forms/netlify.toml +1 -0
  97. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +36 -5
  98. package/corpus/templates/macros/app/routes/extensions._index.tsx +2 -2
  99. package/corpus/templates/macros/netlify.toml +1 -0
  100. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +0 -9
  101. package/corpus/templates/mail/app/routes/extensions._index.tsx +2 -2
  102. package/corpus/templates/mail/netlify.toml +1 -0
  103. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +38 -5
  104. package/corpus/templates/plan/app/routes/extensions._index.tsx +2 -2
  105. package/corpus/templates/plan/netlify.toml +1 -0
  106. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +47 -4
  107. package/corpus/templates/slides/app/routes/extensions._index.tsx +2 -2
  108. package/corpus/templates/slides/netlify.toml +1 -0
  109. package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +37 -6
  110. package/corpus/templates/tasks/app/routes/extensions._index.tsx +2 -2
  111. package/corpus/templates/tasks/app/routes/settings.tsx +31 -0
  112. package/corpus/templates/tasks/netlify.toml +1 -0
  113. package/dist/agent/production-agent.d.ts.map +1 -1
  114. package/dist/agent/production-agent.js +4 -0
  115. package/dist/agent/production-agent.js.map +1 -1
  116. package/dist/agent/run-manager.d.ts +9 -0
  117. package/dist/agent/run-manager.d.ts.map +1 -1
  118. package/dist/agent/run-manager.js +25 -10
  119. package/dist/agent/run-manager.js.map +1 -1
  120. package/dist/agent/types.d.ts +2 -2
  121. package/dist/agent/types.d.ts.map +1 -1
  122. package/dist/agent/types.js.map +1 -1
  123. package/dist/client/extensions/ExtensionsListPage.d.ts +5 -1
  124. package/dist/client/extensions/ExtensionsListPage.d.ts.map +1 -1
  125. package/dist/client/extensions/ExtensionsListPage.js +5 -3
  126. package/dist/client/extensions/ExtensionsListPage.js.map +1 -1
  127. package/dist/client/settings/ExtensionsSettingsContent.d.ts +2 -0
  128. package/dist/client/settings/ExtensionsSettingsContent.d.ts.map +1 -0
  129. package/dist/client/settings/ExtensionsSettingsContent.js +10 -0
  130. package/dist/client/settings/ExtensionsSettingsContent.js.map +1 -0
  131. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  132. package/dist/client/settings/SettingsPanel.js +10 -1
  133. package/dist/client/settings/SettingsPanel.js.map +1 -1
  134. package/dist/collab/awareness.d.ts +2 -2
  135. package/dist/collab/awareness.d.ts.map +1 -1
  136. package/dist/deploy/build.js +1 -0
  137. package/dist/deploy/build.js.map +1 -1
  138. package/dist/observability/routes.d.ts +3 -3
  139. package/dist/progress/routes.d.ts +1 -1
  140. package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
  141. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  142. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  143. package/dist/server/analytics.d.ts +9 -4
  144. package/dist/server/analytics.d.ts.map +1 -1
  145. package/dist/server/analytics.js +111 -22
  146. package/dist/server/analytics.js.map +1 -1
  147. package/dist/templates/chat/app/components/layout/Sidebar.tsx +35 -6
  148. package/dist/templates/chat/app/routes/extensions._index.tsx +2 -2
  149. package/dist/templates/chat/netlify.toml +1 -0
  150. package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
  151. package/dist/vite/client.d.ts.map +1 -1
  152. package/dist/vite/client.js +2 -0
  153. package/dist/vite/client.js.map +1 -1
  154. package/docs/content/extensions.mdx +23 -4
  155. package/package.json +1 -1
  156. package/src/agent/production-agent.ts +4 -0
  157. package/src/agent/run-manager.ts +36 -10
  158. package/src/agent/types.ts +2 -0
  159. package/src/client/extensions/ExtensionsListPage.tsx +16 -3
  160. package/src/client/settings/ExtensionsSettingsContent.tsx +24 -0
  161. package/src/client/settings/SettingsPanel.tsx +10 -0
  162. package/src/deploy/build.ts +3 -0
  163. package/src/server/analytics.ts +154 -25
  164. package/src/templates/chat/app/components/layout/Sidebar.tsx +35 -6
  165. package/src/templates/chat/app/routes/extensions._index.tsx +2 -2
  166. package/src/templates/chat/netlify.toml +1 -0
  167. package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
  168. package/src/vite/client.ts +6 -0
  169. package/corpus/templates/analytics/app/components/layout/NewAnalysisDialog.tsx +0 -165
  170. package/corpus/templates/analytics/app/pages/analyses/AnalysesList.tsx +0 -175
@@ -709,7 +709,7 @@ export default createAgentChatPlugin({
709
709
  // render every organization metric before the model request starts.
710
710
  const sourceGuidance =
711
711
  analyticsSourceGuidanceOpening() +
712
- "DASHBOARD CREATION RULE — You may create dashboards, analyses, SQL panels, or other resources only when the user explicitly asks you to (e.g. 'build me a dashboard for...', 'create a new analysis', 'add a chart for...'). Never create any resource proactively during research, trend analysis, or answering questions. If you think a dashboard would be useful, suggest it and wait for explicit confirmation before creating anything. Never add new items to the sidebar or modify existing dashboards without an explicit user directive. " +
712
+ "DASHBOARD CREATION RULE — You may create dashboard artifacts, SQL panels, or other resources only when the user explicitly asks you to (e.g. 'build me a dashboard for...', 'save this analysis', 'add a chart for...'). Treat a requested saved analysis or deep-dive report as a dashboard request. Never create any resource proactively during research, trend analysis, or answering questions. If you think a dashboard would be useful, suggest it and wait for explicit confirmation before creating anything. Never add new items to the sidebar or modify existing dashboards without an explicit user directive. " +
713
713
  "DASHBOARD MUTATION RULE — For dashboard edits, default to `mutate-dashboard` with the typed `dashboard.*` script API so the main payload is a string and avoids native-array serialization traps. It can move panels by id, edit titles/SQL/config, insert, duplicate, remove, and patch dashboard fields in one atomic save. The script API is constrained: no variables/imports/loops/functions, only JSON-compatible arguments on documented dashboard methods. Do not count shifting `/panels/<index>` positions for ordinary dashboard edits unless the user specifically asks for low-level JSON-pointer operations. " +
714
714
  'DASHBOARD EXTENSION BOX RULE — Analytics dashboards support extension boxes. For ordinary requests such as "put X in this dashboard," insert a `chartType: "extension"` panel with `config.extensionId`; this author-selected embed is shared with the dashboard, appears in scheduled reports, and receives dashboard/panel/current-filter context. Use `config.extensionSlotId` only when the user explicitly asks for a personal/per-viewer slot. Slot ids use `analytics.dashboard.<dashboard-id>.panel.<panel-id>` and require `add-extension-slot-target` plus `install-extension`; installs are per-user, so viewers can see different content and report identities may see an empty slot. Use `get-sql-dashboard` panel summaries to inspect an existing box. ' +
715
715
  'EXTENSION DATA-REPAIR RULE — When fixing data in an existing extension-backed dashboard or migrated surface such as Risk Meeting, inspect the current dashboard and extension first, then call `update-extension` with exactly `id`, `operation="edit"`, and a `payloadJson` string containing focused patches/edits that change only the data-loading seam. Never send empty placeholder fields. Preserve the existing layout, CSS, copy, and interactions; never reconstruct the full HTML body for a data-only fix. A request that combines a visual rewrite such as compacting, removing sections, renaming, or changing padding with a data repair is a broad rewrite; after inspecting the current extension, use `operation="replace"` with the complete replacement in `payloadJson`. If a focused edit fails, change the target instead of retrying identical arguments. ' +
@@ -721,20 +721,19 @@ export default createAgentChatPlugin({
721
721
  "When the user names a provider such as first-party Analytics, BigQuery, HubSpot, Gong, Jira, Pylon, Slack, Sentry, GA4, or another connected source, that source is authoritative for the turn. Use its first-class query action when available; if it is not on the initial tool surface, use tool-search for that provider instead of loading unrelated catalogs. For an ordinary structured lookup, make one bounded query and stop on success. " +
722
722
  "Load provider API, corpus, staging, or code tools only when the user explicitly requests cross-source work, exhaustive unstructured-record coverage, or an absence claim that the first-class action cannot support. For those genuinely broad workflows, fetch every relevant page or an explicitly bounded cohort, preserve coverage counts, and state any uncovered records. " +
723
723
  "For named deal, account, renewal, churn-risk, or customer deep dives that need HubSpot and Gong context, `account-deep-dive` can provide a bounded evidence bundle. Do not answer a requested transcript deep dive from call metadata alone. " +
724
- "When the user refers to the current analysis, this analysis, this project, or asks to spin off, adapt, modify, or reuse a saved analysis, call `view-screen` first and use the returned analysis details; if an analysis id or @mention is provided, call `get-analysis` before responding. " +
724
+ "When the user refers to the current dashboard artifact, this analysis, this project, or asks to spin off, adapt, modify, or reuse a saved analysis, call `view-screen` first and use the returned dashboard details; for an explicitly named legacy analysis id, call `get-analysis` before responding and preserve its legacy deep link only for compatibility. " +
725
725
  "If a query action fails because its arguments are invalid, correct the arguments once. Never repeat the identical failed call. For credential, permission, quota, network, or repeated schema failures, stop using that source for the turn and surface the actual error instead of trying unrelated providers. " +
726
726
  "For ordinary ad-hoc structured data questions, answer the explicit question after the first relevant successful query or bounded evidence batch. The words all, total, or exact do not require cross-source validation when a single structured query fully covers the requested source and filters. " +
727
- "If the user challenges coverage, asks why more records were not included, or asks for the updated answer, rerun the relevant source query or revise from the corrected cohort and provide the updated deliverable directly. Do not claim an analysis was revised unless the revised answer is included in the response or saved with `save-analysis`. " +
727
+ "If the user challenges coverage, asks why more records were not included, or asks for the updated answer, rerun the relevant source query or revise from the corrected cohort and provide the updated deliverable directly. Do not claim a dashboard artifact was revised unless the revised answer is included in the response or saved with `update-dashboard`. " +
728
728
  "Unstructured source records are valid analytics evidence: Pylon tickets, Jira issues, Gong calls/transcripts, Slack messages, and similar text records may be coded for themes, mention counts, sentiment, objections, and qualitative patterns as long as the answer states the inspected sample size and does not imply unsupported statistical certainty. " +
729
729
  "For schema questions, prefer data-dictionary entries and configured warehouse schemas over assumptions; use `search-bigquery-schema` for BigQuery metadata before inventing datasets, tables, or columns. " +
730
730
  "Before finalizing any analytics answer, make the evidence trail explicit enough to audit: answer the user's question, name the source(s), time window, sample size or row count, filters, join/match method, caveats/gaps, and recommended next action when useful. Never substitute fabricated numbers for a failed query or unavailable provider. It is fine to ask a clarifying question, provide a plan, or say exactly which source is unavailable as long as you do not present metrics or source-record conclusions without evidence.\n" +
731
731
  "</data-source-guidance>";
732
732
  const artifactGuidance =
733
733
  "<analytics-artifact-guidance>\n" +
734
- "Native Analytics dashboards and saved analyses are constrained artifacts: dashboards are JSON configs rendered by the built-in dashboard components, and analyses are Markdown reports with generated chart images plus structured resultData. " +
735
- "If the user's requested dashboard, analysis surface, visualization, interaction model, custom layout, or bespoke workflow cannot be faithfully represented within those native components/config fields, do not hand-wave, force an approximate JSON dashboard, or route to source-code changes. In production mode, automatically create a sandboxed extension with `create-extension` instead, using Alpine.js HTML and the available app/data helpers. " +
736
- "After creating the extension, briefly tell the user that the request needed bespoke UI/code beyond the native Analytics dashboard or analysis format, so you built it as an extension. " +
737
- "Do not also create a same-named dashboard or saved analysis unless the user explicitly asked for multiple artifacts; saved analyses appear in the sidebar and should not be used for throwaway notes or scratch summaries.\n" +
734
+ "Analytics has one user-facing artifact type: dashboards. Dashboards are JSON configs rendered by the built-in dashboard components, and an extension is a dashboard block—not a separate Analytics result. " +
735
+ 'If the user\'s requested dashboard, saved analysis/report, visualization, interaction model, custom layout, or bespoke workflow cannot be faithfully represented within the native dashboard components/config fields, do not hand-wave, force an approximate JSON dashboard, or route to source-code changes. In production mode, automatically create a sandboxed extension with `create-extension`, then immediately call `update-dashboard` to embed it as one or more `chartType: "extension"` panels with `config.extensionId`. ' +
736
+ "After creating the dashboard, briefly tell the user that a dashboard block uses an embedded extension because the request needed bespoke UI/code. Never leave a newly created extension standalone, never create a separate saved analysis for the same request, and never direct the user to an Extensions page from Analytics. Legacy analyses may be read or updated only for compatibility with an existing deep link.\n" +
738
737
  "</analytics-artifact-guidance>";
739
738
 
740
739
  return `${sourceGuidance}\n\n${artifactGuidance}\n\n${analyticsDataDictionaryRoutingContext()}`;
@@ -779,50 +778,5 @@ export default createAgentChatPlugin({
779
778
  }
780
779
  },
781
780
  },
782
- analyses: {
783
- label: "Analyses",
784
- icon: "document",
785
- search: async (query: string, event?: any) => {
786
- if (!event) return [];
787
- try {
788
- const { getOrgContext } = await import("@agent-native/core/org");
789
- const { listAnalyses } = await import("../lib/dashboards-store.js");
790
- const ctx = await getOrgContext(event);
791
- const rows = await listAnalyses({
792
- email: ctx.email,
793
- orgId: ctx.orgId ?? null,
794
- });
795
- const q = (query || "").toLowerCase().trim();
796
- const filtered = q
797
- ? rows.filter(
798
- (analysis) =>
799
- (analysis.name || "").toLowerCase().includes(q) ||
800
- (analysis.description || "").toLowerCase().includes(q) ||
801
- analysis.id.toLowerCase().includes(q),
802
- )
803
- : rows;
804
-
805
- return filtered
806
- .sort(
807
- (a, b) =>
808
- new Date(b.updatedAt).getTime() -
809
- new Date(a.updatedAt).getTime(),
810
- )
811
- .slice(0, 20)
812
- .map((analysis) => ({
813
- id: `analysis:${analysis.id}`,
814
- label: analysis.name || "Untitled analysis",
815
- description: `/analyses/${analysis.id}`,
816
- icon: "document",
817
- refType: "analysis",
818
- refId: analysis.id,
819
- refPath: `/analyses/${analysis.id}`,
820
- }));
821
- } catch (err) {
822
- console.error("[analytics] Analysis mention provider failed:", err);
823
- return [];
824
- }
825
- },
826
- },
827
781
  },
828
782
  });
@@ -10,7 +10,7 @@ export default createCoreRoutesPlugin({
10
10
  resolveOpenPath: ({ view, params }) => {
11
11
  if (params.dashboardId) return `/dashboards/${params.dashboardId}`;
12
12
  if (params.analysisId) return `/analyses/${params.analysisId}`;
13
- if (view === "analyses") return "/analyses";
13
+ if (view === "analyses") return "/dashboards";
14
14
  // `adhoc`/unknown with no id: there is no bare `/dashboards` record route —
15
15
  // send to the app root rather than 404 (the polled `navigate` command
16
16
  // still applies any record focus once the SPA is loaded).
@@ -1265,6 +1265,16 @@ const runAnalyticsMigrations = runMigrations(
1265
1265
  name: "error-events-user-key-filter-idx",
1266
1266
  sql: `CREATE INDEX IF NOT EXISTS error_events_user_key_filter_idx ON error_events (user_key, owner_email, org_id, issue_id)`,
1267
1267
  },
1268
+ {
1269
+ version: 121,
1270
+ name: "analytics-events-org-path-event-idx",
1271
+ sql: `CREATE INDEX IF NOT EXISTS analytics_events_org_path_event_idx ON analytics_events (org_id, path, event_name)`,
1272
+ },
1273
+ {
1274
+ version: 122,
1275
+ name: "dashboard-revisions-org-dashboard-idx",
1276
+ sql: `CREATE INDEX IF NOT EXISTS dashboard_revisions_org_dashboard_idx ON dashboard_revisions (org_id, dashboard_id)`,
1277
+ },
1268
1278
  ],
1269
1279
  { table: "analytics_migrations" },
1270
1280
  );
@@ -33,13 +33,47 @@ const presetReferenceFillSchema = z.object({
33
33
  assetIds: z.array(z.string().min(1)).min(1).max(4),
34
34
  });
35
35
 
36
+ const imageBatchAgentInputSchema = z.object({
37
+ libraryId: z
38
+ .string()
39
+ .min(1)
40
+ .describe("Brand kit/library ID to use for every generated image."),
41
+ collectionId: z.string().optional(),
42
+ presetId: z.string().optional(),
43
+ presetReferenceFills: z.array(presetReferenceFillSchema).max(6).optional(),
44
+ sessionId: z.string().optional(),
45
+ slots: z
46
+ .array(
47
+ z.object({
48
+ slotId: z.string(),
49
+ prompt: z.string().min(1),
50
+ embeddedText: z.string().optional(),
51
+ textPlacement: z.string().optional(),
52
+ aspectRatio: z.enum(ASPECT_RATIOS).optional(),
53
+ imageSize: z.enum(IMAGE_SIZES).optional(),
54
+ categories: z.array(z.enum(IMAGE_CATEGORIES)).optional(),
55
+ referenceAssetIds: z.array(z.string()).optional(),
56
+ sourceAssetId: z.string().optional(),
57
+ subjectAssetId: z.string().optional(),
58
+ intent: z.enum(GENERATION_INTENTS).optional(),
59
+ styleStrength: z.enum(STYLE_STRENGTHS).optional(),
60
+ }),
61
+ )
62
+ .min(1)
63
+ .max(12),
64
+ model: z.enum(IMAGE_MODELS).optional(),
65
+ tier: z.enum(IMAGE_QUALITY_TIERS).optional(),
66
+ includeLogo: z.boolean().optional(),
67
+ groundingMode: z.enum(["auto", "off", "google-search"]).optional(),
68
+ });
69
+
36
70
  export default defineAction({
37
71
  description:
38
72
  "Generate several brand-consistent images in parallel from one brand kit/library. Use @brand-kit mentions as libraryId and @preset mentions as presetId when present. If no preset is tagged, call list-generation-presets first and use a matching preset's presetId; the user may not know presets exist. Generate presetless only when no preset matches the request. This is synchronous for images: one call waits for every slot and returns final image artifacts. Use this for slide decks, landing pages, and multi-slot design work. Do not call get-generation-run or refresh-generation-run after a normal image batch result.",
39
73
  schema: z.object({
40
74
  libraryId: z
41
75
  .string()
42
- .optional()
76
+ .min(1)
43
77
  .describe(
44
78
  "Brand kit/library ID. Pass the refId from a brand-kit @mention, or choose a kit from view-screen/list-libraries.",
45
79
  ),
@@ -135,6 +169,7 @@ export default defineAction({
135
169
  "Disable Creative Context for this batch only without changing the saved preference.",
136
170
  ),
137
171
  }),
172
+ agentInputSchema: imageBatchAgentInputSchema,
138
173
  parallelSafe: true,
139
174
  timeoutMs: IMAGE_GENERATION_TOOL_TIMEOUT_MS,
140
175
  run: async ({ slots, ...inputBase }, context?: ActionRunContext) => {
@@ -83,13 +83,39 @@ const presetReferenceFillSchema = z.object({
83
83
  assetIds: z.array(z.string().min(1)).min(1).max(4),
84
84
  });
85
85
 
86
+ const imageGenerationAgentInputSchema = z.object({
87
+ libraryId: z
88
+ .string()
89
+ .min(1)
90
+ .describe("Brand kit/library ID to use for this image."),
91
+ collectionId: z.string().optional(),
92
+ presetId: z.string().optional(),
93
+ sessionId: z.string().optional(),
94
+ prompt: z.string().min(1),
95
+ embeddedText: z.string().optional(),
96
+ textPlacement: z.string().optional(),
97
+ aspectRatio: z.enum(ASPECT_RATIOS).optional(),
98
+ imageSize: z.enum(IMAGE_SIZES).optional(),
99
+ model: z.enum(IMAGE_MODELS).optional(),
100
+ tier: z.enum(IMAGE_QUALITY_TIERS).optional(),
101
+ intent: z.enum(GENERATION_INTENTS).optional(),
102
+ styleStrength: z.enum(STYLE_STRENGTHS).optional(),
103
+ categories: z.array(z.enum(IMAGE_CATEGORIES)).optional(),
104
+ referenceAssetIds: z.array(z.string()).optional(),
105
+ presetReferenceFills: z.array(presetReferenceFillSchema).max(6).optional(),
106
+ includeLogo: z.boolean().optional(),
107
+ sourceAssetId: z.string().optional(),
108
+ subjectAssetId: z.string().optional(),
109
+ groundingMode: z.enum(["auto", "off", "google-search"]).optional(),
110
+ });
111
+
86
112
  export default defineAction({
87
113
  description:
88
114
  "Generate one brand-consistent image from a brand kit/library. This is synchronous for images and returns the final asset with preview/download/embed URLs. Use @brand-kit mentions as libraryId and @preset mentions as presetId when present. If no preset is tagged, call list-generation-presets first and use a matching preset's presetId; the user may not know presets exist. Generate presetless only when no preset matches the request. Use generate-image-batch for multiple independent slots; do not poll image runs after this action returns.",
89
115
  schema: z.object({
90
116
  libraryId: z
91
117
  .string()
92
- .optional()
118
+ .min(1)
93
119
  .describe(
94
120
  "Brand kit/library ID. Pass the refId from a brand-kit @mention, or choose a kit from view-screen/list-libraries.",
95
121
  ),
@@ -205,6 +231,7 @@ export default defineAction({
205
231
  "When false, attach the output to the session without making it the active asset. Batch generation selects the active asset deterministically after all slots finish.",
206
232
  ),
207
233
  }),
234
+ agentInputSchema: imageGenerationAgentInputSchema,
208
235
  parallelSafe: true,
209
236
  timeoutMs: IMAGE_GENERATION_TOOL_TIMEOUT_MS,
210
237
  run: async (input, context?: ActionRunContext) => {
@@ -6,7 +6,6 @@ import {
6
6
  } from "@agent-native/core/client/agent-chat";
7
7
  import { appPath } from "@agent-native/core/client/api-path";
8
8
  import { DevDatabaseLink } from "@agent-native/core/client/db-admin";
9
- import { ExtensionsSidebarSection } from "@agent-native/core/client/extensions";
10
9
  import { useActionQuery } from "@agent-native/core/client/hooks";
11
10
  import { useT } from "@agent-native/core/client/i18n";
12
11
  import { OrgSwitcher } from "@agent-native/core/client/org";
@@ -40,6 +39,9 @@ import { cn } from "@/lib/utils";
40
39
  const baseNavItems = [
41
40
  { icon: IconPhotoPlus, labelKey: "navigation.create", href: "/" },
42
41
  { icon: IconLayoutGrid, labelKey: "navigation.library", href: "/library" },
42
+ ];
43
+
44
+ const bottomNavItems = [
43
45
  { icon: IconHierarchy2, labelKey: "settings.agentTitle", href: "/agent" },
44
46
  { icon: IconSettings, labelKey: "navigation.settings", href: "/settings" },
45
47
  ];
@@ -448,22 +450,58 @@ export function Sidebar() {
448
450
  })}
449
451
  </nav>
450
452
 
451
- {!collapsed && (
452
- <div className="mt-auto shrink-0">
453
- <div className="px-2 py-1">
454
- <ExtensionsSidebarSection />
455
- </div>
453
+ <div className="mt-auto shrink-0">
454
+ <nav
455
+ className={cn(
456
+ "grid gap-1",
457
+ collapsed ? "justify-items-center px-1.5 py-1" : "px-2 py-1",
458
+ )}
459
+ >
460
+ {bottomNavItems.map((item) => {
461
+ const Icon = item.icon;
462
+ const isActive = location.pathname.startsWith(item.href);
463
+ const link = (
464
+ <Link
465
+ to={item.href}
466
+ className={cn(
467
+ "flex items-center rounded-lg text-sm",
468
+ collapsed ? "h-9 w-9 justify-center" : "gap-3 px-3 py-2",
469
+ isActive
470
+ ? "bg-sidebar-accent text-sidebar-accent-foreground"
471
+ : "text-muted-foreground hover:bg-sidebar-accent/50 hover:text-foreground",
472
+ )}
473
+ aria-label={collapsed ? t(item.labelKey) : undefined}
474
+ >
475
+ <Icon className="h-4 w-4 shrink-0" />
476
+ {!collapsed && t(item.labelKey)}
477
+ </Link>
478
+ );
479
+ return collapsed ? (
480
+ <Tooltip key={item.href} delayDuration={0}>
481
+ <TooltipTrigger asChild>{link}</TooltipTrigger>
482
+ <TooltipContent side="right">
483
+ {t(item.labelKey)}
484
+ </TooltipContent>
485
+ </Tooltip>
486
+ ) : (
487
+ <div key={item.href}>{link}</div>
488
+ );
489
+ })}
490
+ </nav>
456
491
 
492
+ {!collapsed && (
457
493
  <div className="px-3 py-2">
458
494
  <OrgSwitcher />
459
495
  </div>
496
+ )}
460
497
 
498
+ {!collapsed && (
461
499
  <div className="px-3 py-2 empty:hidden">
462
500
  <DevDatabaseLink />
463
501
  <FeedbackButton />
464
502
  </div>
465
- </div>
466
- )}
503
+ )}
504
+ </div>
467
505
  </div>
468
506
  </aside>
469
507
  );
@@ -1,4 +1,4 @@
1
- import { ExtensionsListPage } from "@agent-native/core/client/extensions";
1
+ import { Navigate } from "react-router";
2
2
 
3
3
  import { messagesByLocale } from "@/i18n-data";
4
4
 
@@ -7,5 +7,5 @@ export function meta() {
7
7
  }
8
8
 
9
9
  export default function ExtensionsRoute() {
10
- return <ExtensionsListPage />;
10
+ return <Navigate to="/settings#extensions" replace />;
11
11
  }
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-23
4
+ ---
5
+
6
+ Assets image generation now recovers after a completed tool run.
@@ -6,6 +6,7 @@ functions = "templates/assets/.netlify/functions-internal"
6
6
 
7
7
  [build.environment]
8
8
  GA_MEASUREMENT_ID = "G-ESF7FYXGN9"
9
+ GTM_CONTAINER_ID = "GTM-N3WSTXZ"
9
10
  NITRO_PRESET = "netlify"
10
11
  NPM_CONFIG_PRODUCTION = "false"
11
12
  AGENT_CHAT_DURABLE_BACKGROUND = "true"
@@ -5,7 +5,6 @@ import {
5
5
  } from "@agent-native/core/client/agent-chat";
6
6
  import { appPath } from "@agent-native/core/client/api-path";
7
7
  import { DevDatabaseLink } from "@agent-native/core/client/db-admin";
8
- import { ExtensionsSidebarSection } from "@agent-native/core/client/extensions";
9
8
  import { useT } from "@agent-native/core/client/i18n";
10
9
  import { OrgSwitcher } from "@agent-native/core/client/org";
11
10
  import { FeedbackButton } from "@agent-native/core/client/ui";
@@ -29,6 +28,13 @@ import {
29
28
  import { navItems } from "@/lib/brain";
30
29
  import { cn } from "@/lib/utils";
31
30
 
31
+ const primaryNavItems = navItems.filter(
32
+ (item) => item.view !== "agent" && item.view !== "settings",
33
+ );
34
+ const bottomNavItems = navItems.filter(
35
+ (item) => item.view === "agent" || item.view === "settings",
36
+ );
37
+
32
38
  const BRAIN_CHAT_STORAGE_KEY = "brain";
33
39
  const BRAIN_ACTIVE_THREAD_KEY = `agent-chat-active-thread:${BRAIN_CHAT_STORAGE_KEY}`;
34
40
 
@@ -311,7 +317,7 @@ export function Sidebar({
311
317
  collapsed ? "justify-items-center gap-1" : "gap-1",
312
318
  )}
313
319
  >
314
- {navItems.map((item) => {
320
+ {primaryNavItems.map((item) => {
315
321
  const Icon = item.icon;
316
322
  const label =
317
323
  item.view === "agent"
@@ -362,13 +368,37 @@ export function Sidebar({
362
368
  </div>
363
369
  </nav>
364
370
 
365
- <div className="mt-auto shrink-0">
366
- {!collapsed ? (
367
- <div className="px-2 py-1">
368
- <ExtensionsSidebarSection />
369
- </div>
370
- ) : null}
371
+ <nav className="grid shrink-0 gap-1 px-2 py-1">
372
+ {bottomNavItems.map((item) => {
373
+ const Icon = item.icon;
374
+ const label =
375
+ item.view === "agent"
376
+ ? t("settings.agentTitle")
377
+ : t(`navigation.${item.view}`);
378
+ const link = (
379
+ <NavLink
380
+ to={item.href}
381
+ className={navClass}
382
+ aria-label={collapsed ? label : undefined}
383
+ >
384
+ <Icon className="size-4 shrink-0" />
385
+ <span className={collapsed ? "sr-only" : "truncate"}>
386
+ {label}
387
+ </span>
388
+ </NavLink>
389
+ );
390
+ return collapsed ? (
391
+ <Tooltip key={item.href}>
392
+ <TooltipTrigger asChild>{link}</TooltipTrigger>
393
+ <TooltipContent side="right">{label}</TooltipContent>
394
+ </Tooltip>
395
+ ) : (
396
+ <div key={item.href}>{link}</div>
397
+ );
398
+ })}
399
+ </nav>
371
400
 
401
+ <div className="mt-auto shrink-0">
372
402
  {!collapsed ? (
373
403
  <div className="px-3 py-2">
374
404
  <OrgSwitcher reserveSpace />
@@ -1,4 +1,4 @@
1
- import { ExtensionsListPage } from "@agent-native/core/client/extensions";
1
+ import { Navigate } from "react-router";
2
2
 
3
3
  import { messagesByLocale } from "@/i18n-data";
4
4
 
@@ -7,5 +7,5 @@ export function meta() {
7
7
  }
8
8
 
9
9
  export default function ExtensionsRoute() {
10
- return <ExtensionsListPage />;
10
+ return <Navigate to="/settings#extensions" replace />;
11
11
  }
@@ -6,6 +6,7 @@ functions = "templates/brain/.netlify/functions-internal"
6
6
 
7
7
  [build.environment]
8
8
  GA_MEASUREMENT_ID = "G-ESF7FYXGN9"
9
+ GTM_CONTAINER_ID = "GTM-N3WSTXZ"
9
10
  NITRO_PRESET = "netlify"
10
11
  NPM_CONFIG_PRODUCTION = "false"
11
12
 
@@ -1,6 +1,5 @@
1
1
  import { appPath } from "@agent-native/core/client/api-path";
2
2
  import { DevDatabaseLink } from "@agent-native/core/client/db-admin";
3
- import { ExtensionsSidebarSection } from "@agent-native/core/client/extensions";
4
3
  import { useT } from "@agent-native/core/client/i18n";
5
4
  import { OrgSwitcher } from "@agent-native/core/client/org";
6
5
  import { FeedbackButton } from "@agent-native/core/client/ui";
@@ -96,6 +95,9 @@ const navItems = [
96
95
  labelKey: "navigation.bookingLinks",
97
96
  icon: IconLink,
98
97
  },
98
+ ];
99
+
100
+ const bottomNavItems = [
99
101
  {
100
102
  path: "/agent",
101
103
  labelKey: "settings.agentTitle",
@@ -1123,12 +1125,48 @@ export function Sidebar({
1123
1125
  )}
1124
1126
  </div>
1125
1127
 
1128
+ <nav
1129
+ className={cn(
1130
+ "shrink-0",
1131
+ collapsed
1132
+ ? "flex flex-col items-center gap-1 px-1 py-2"
1133
+ : "space-y-0.5 border-t border-border p-2.5",
1134
+ )}
1135
+ >
1136
+ {bottomNavItems.map((item) => {
1137
+ const isActive = location.pathname.startsWith(item.path);
1138
+ const link = (
1139
+ <Link
1140
+ to={item.path}
1141
+ onClick={onClose}
1142
+ aria-label={collapsed ? t(item.labelKey) : undefined}
1143
+ className={cn(
1144
+ "flex items-center rounded-lg font-medium transition-colors",
1145
+ collapsed
1146
+ ? "h-10 w-10 justify-center"
1147
+ : "gap-3 px-3 py-2 text-sm",
1148
+ isActive
1149
+ ? "bg-primary/10 text-primary"
1150
+ : "text-muted-foreground hover:bg-accent/60 hover:text-foreground",
1151
+ )}
1152
+ >
1153
+ <item.icon className="h-4 w-4" />
1154
+ {!collapsed && t(item.labelKey)}
1155
+ </Link>
1156
+ );
1157
+ return collapsed ? (
1158
+ <Tooltip key={item.path}>
1159
+ <TooltipTrigger asChild>{link}</TooltipTrigger>
1160
+ <TooltipContent side="right">{t(item.labelKey)}</TooltipContent>
1161
+ </Tooltip>
1162
+ ) : (
1163
+ <div key={item.path}>{link}</div>
1164
+ );
1165
+ })}
1166
+ </nav>
1167
+
1126
1168
  {!collapsed ? (
1127
1169
  <div className="shrink-0">
1128
- <div className="px-2.5 py-1.5">
1129
- <ExtensionsSidebarSection />
1130
- </div>
1131
-
1132
1170
  <div className="px-3 py-2">
1133
1171
  <OrgSwitcher reserveSpace />
1134
1172
  </div>
@@ -1,19 +1,5 @@
1
- import { ExtensionsListPage } from "@agent-native/core/client/extensions";
2
- import { useMemo } from "react";
3
-
4
- import { useAppHeaderControls } from "@/components/layout/AppLayout";
1
+ import { Navigate } from "react-router";
5
2
 
6
3
  export default function ExtensionsRoute() {
7
- const controls = useMemo(
8
- () => ({
9
- left: (
10
- <h1 className="text-lg font-semibold tracking-tight truncate">
11
- Extensions
12
- </h1>
13
- ),
14
- }),
15
- [],
16
- );
17
- useAppHeaderControls(controls);
18
- return <ExtensionsListPage />;
4
+ return <Navigate to="/settings#extensions" replace />;
19
5
  }
@@ -6,6 +6,7 @@ functions = "templates/calendar/.netlify/functions-internal"
6
6
 
7
7
  [build.environment]
8
8
  GA_MEASUREMENT_ID = "G-ESF7FYXGN9"
9
+ GTM_CONTAINER_ID = "GTM-N3WSTXZ"
9
10
  NITRO_PRESET = "netlify"
10
11
  NPM_CONFIG_PRODUCTION = "false"
11
12
 
@@ -4,7 +4,6 @@ import {
4
4
  type ChatThreadSummary,
5
5
  } from "@agent-native/core/client/agent-chat";
6
6
  import { appPath } from "@agent-native/core/client/api-path";
7
- import { ExtensionsSidebarSection } from "@agent-native/core/client/extensions";
8
7
  import { useT } from "@agent-native/core/client/i18n";
9
8
  import { OrgSwitcher } from "@agent-native/core/client/org";
10
9
  import { FeedbackButton } from "@agent-native/core/client/ui";
@@ -38,6 +37,9 @@ const navItems = [
38
37
  href: "/",
39
38
  view: "chat",
40
39
  },
40
+ ];
41
+
42
+ const bottomNavItems = [
41
43
  {
42
44
  icon: IconHierarchy2,
43
45
  labelKey: "settings.agentTitle",
@@ -419,11 +421,38 @@ export function Sidebar({
419
421
  </nav>
420
422
 
421
423
  <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}
424
+ <nav
425
+ className={cn(
426
+ "grid",
427
+ collapsed ? "gap-0 px-1 py-1" : "gap-1 px-2 py-1",
428
+ )}
429
+ >
430
+ {bottomNavItems.map((item) => {
431
+ const Icon = item.icon;
432
+ const isActive = location.pathname.startsWith(item.href);
433
+ const link = (
434
+ <Link
435
+ to={item.href}
436
+ className={navClass({ isActive })}
437
+ aria-current={isActive ? "page" : undefined}
438
+ aria-label={collapsed ? t(item.labelKey) : undefined}
439
+ >
440
+ <Icon className="size-4 shrink-0" />
441
+ <span className={collapsed ? "sr-only" : "truncate"}>
442
+ {t(item.labelKey)}
443
+ </span>
444
+ </Link>
445
+ );
446
+ return collapsed ? (
447
+ <Tooltip key={item.href}>
448
+ <TooltipTrigger asChild>{link}</TooltipTrigger>
449
+ <TooltipContent side="right">{t(item.labelKey)}</TooltipContent>
450
+ </Tooltip>
451
+ ) : (
452
+ <div key={item.href}>{link}</div>
453
+ );
454
+ })}
455
+ </nav>
427
456
 
428
457
  <div className={cn(collapsed ? "px-1 py-1" : "px-3 py-2")}>
429
458
  <OrgSwitcher
@@ -1,4 +1,4 @@
1
- import { ExtensionsListPage } from "@agent-native/core/client/extensions";
1
+ import { Navigate } from "react-router";
2
2
 
3
3
  import { APP_TITLE } from "@/lib/app-config";
4
4
 
@@ -7,5 +7,5 @@ export function meta() {
7
7
  }
8
8
 
9
9
  export default function ExtensionsRoute() {
10
- return <ExtensionsListPage />;
10
+ return <Navigate to="/settings#extensions" replace />;
11
11
  }
@@ -6,5 +6,6 @@ functions = "templates/chat/.netlify/functions-internal"
6
6
 
7
7
  [build.environment]
8
8
  GA_MEASUREMENT_ID = "G-ESF7FYXGN9"
9
+ GTM_CONTAINER_ID = "GTM-N3WSTXZ"
9
10
  NITRO_PRESET = "netlify"
10
11
  NPM_CONFIG_PRODUCTION = "false"