@agent-native/core 0.77.24 → 0.78.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +20 -0
  3. package/corpus/core/docs/content/template-analytics.md +5 -0
  4. package/corpus/core/docs/content/tracking.md +45 -0
  5. package/corpus/core/package.json +3 -1
  6. package/corpus/core/src/agent/types.ts +22 -0
  7. package/corpus/core/src/client/AgentPanel.tsx +34 -18
  8. package/corpus/core/src/client/AssistantChat.tsx +40 -0
  9. package/corpus/core/src/client/agent-chat.ts +173 -0
  10. package/corpus/core/src/client/analytics.ts +141 -5
  11. package/corpus/core/src/client/composer/TiptapComposer.tsx +316 -60
  12. package/corpus/core/src/client/composer/extensions/MentionReference.tsx +3 -0
  13. package/corpus/core/src/client/composer/types.ts +22 -0
  14. package/corpus/core/src/client/index.ts +14 -0
  15. package/corpus/core/src/client/session-replay.ts +800 -0
  16. package/corpus/core/src/db/schema.ts +12 -0
  17. package/corpus/core/src/deploy/build.ts +75 -0
  18. package/corpus/core/src/private-blob/index.ts +18 -0
  19. package/corpus/core/src/private-blob/registry.ts +241 -0
  20. package/corpus/core/src/private-blob/types.ts +55 -0
  21. package/corpus/core/src/scripts/utils.ts +34 -6
  22. package/corpus/core/src/server/agent-chat-plugin.ts +10 -0
  23. package/corpus/core/src/server/email.ts +5 -0
  24. package/corpus/core/src/server/index.ts +1 -0
  25. package/corpus/core/src/styles/agent-native.css +24 -0
  26. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +1 -1
  27. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +43 -0
  28. package/corpus/templates/analytics/AGENTS.md +32 -1
  29. package/corpus/templates/analytics/DEVELOPING.md +9 -1
  30. package/corpus/templates/analytics/actions/delete-dashboard-report-subscription.ts +24 -0
  31. package/corpus/templates/analytics/actions/get-session-replay-events.ts +50 -0
  32. package/corpus/templates/analytics/actions/get-session-replay-summary.ts +27 -0
  33. package/corpus/templates/analytics/actions/list-dashboard-report-subscriptions.ts +24 -0
  34. package/corpus/templates/analytics/actions/list-session-recordings.ts +59 -0
  35. package/corpus/templates/analytics/actions/navigate.ts +15 -5
  36. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +2 -2
  37. package/corpus/templates/analytics/actions/save-dashboard-report-subscription.ts +36 -0
  38. package/corpus/templates/analytics/actions/send-dashboard-report-now.ts +60 -0
  39. package/corpus/templates/analytics/actions/view-screen.ts +50 -2
  40. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +51 -2
  41. package/corpus/templates/analytics/app/components/layout/Header.tsx +4 -1
  42. package/corpus/templates/analytics/app/components/layout/Layout.tsx +14 -2
  43. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +6 -6
  44. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +35 -6
  45. package/corpus/templates/analytics/app/i18n-data.ts +1119 -0
  46. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/EmailReportDialog.tsx +437 -0
  47. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +11 -12
  48. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +43 -0
  49. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +136 -43
  50. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/report-filters.ts +26 -0
  51. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +450 -0
  52. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +419 -0
  53. package/corpus/templates/analytics/app/routes/overview.tsx +13 -6
  54. package/corpus/templates/analytics/app/routes/sessions.$recordingId.tsx +10 -0
  55. package/corpus/templates/analytics/app/routes/sessions._index.tsx +10 -0
  56. package/corpus/templates/analytics/changelog/2026-06-25-analytics-now-opens-directly-to-ask-instead-of-the-old-overv.md +6 -0
  57. package/corpus/templates/analytics/changelog/2026-06-25-dashboard-charts-now-show-reliable-blue-placement-lines-whil.md +6 -0
  58. package/corpus/templates/analytics/changelog/2026-06-26-dashboards-can-now-send-scheduled-daily-email-reports-from-t.md +6 -0
  59. package/corpus/templates/analytics/changelog/2026-06-26-session-replay-browser-sessions-can-now-be-reviewed-from.md +6 -0
  60. package/corpus/templates/analytics/package.json +5 -2
  61. package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +131 -0
  62. package/corpus/templates/analytics/scripts/seed-session-replay.ts +198 -0
  63. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +140 -27
  64. package/corpus/templates/analytics/server/db/index.ts +12 -0
  65. package/corpus/templates/analytics/server/db/schema.ts +131 -0
  66. package/corpus/templates/analytics/server/handlers/session-replay.ts +281 -0
  67. package/corpus/templates/analytics/server/handlers/sql-query.ts +8 -348
  68. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +66 -0
  69. package/corpus/templates/analytics/server/jobs/session-replay-retention.ts +29 -0
  70. package/corpus/templates/analytics/server/lib/dashboard-panel-query.ts +365 -0
  71. package/corpus/templates/analytics/server/lib/dashboard-report-subscriptions.ts +473 -0
  72. package/corpus/templates/analytics/server/lib/dashboard-report.ts +405 -0
  73. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +63 -18
  74. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +98 -26
  75. package/corpus/templates/analytics/server/lib/session-replay.ts +1645 -0
  76. package/corpus/templates/analytics/server/plugins/auth.ts +1 -1
  77. package/corpus/templates/analytics/server/plugins/dashboard-report-jobs.ts +47 -0
  78. package/corpus/templates/analytics/server/plugins/db.ts +200 -0
  79. package/corpus/templates/analytics/server/plugins/session-replay-retention-jobs.ts +41 -0
  80. package/corpus/templates/analytics/server/routes/api/analytics/replay.options.ts +1 -0
  81. package/corpus/templates/analytics/server/routes/api/analytics/replay.post.ts +1 -0
  82. package/corpus/templates/analytics/server/routes/api/dashboard-reports/run.post.ts +61 -0
  83. package/corpus/templates/analytics/server/routes/api/session-replay/recordings/[recordingId]/chunks/[seq].get.ts +1 -0
  84. package/corpus/templates/analytics/server/routes/api/session-replay/recordings/[recordingId]/events.get.ts +1 -0
  85. package/corpus/templates/analytics/server/routes/api/session-replay/recordings/[recordingId]/manifest.get.ts +1 -0
  86. package/corpus/templates/analytics/server/routes/api/session-replay/recordings/[recordingId].get.ts +1 -0
  87. package/corpus/templates/analytics/server/routes/api/session-replay/recordings.get.ts +1 -0
  88. package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +10 -2
  89. package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +14 -7
  90. package/corpus/templates/assets/.agents/skills/library-management/SKILL.md +12 -0
  91. package/corpus/templates/assets/AGENTS.md +18 -3
  92. package/corpus/templates/assets/actions/_image-model-default.ts +24 -0
  93. package/corpus/templates/assets/actions/dismiss-variant-slots.ts +15 -22
  94. package/corpus/templates/assets/actions/generate-asset.ts +30 -23
  95. package/corpus/templates/assets/actions/generate-image-batch.ts +76 -29
  96. package/corpus/templates/assets/actions/generate-image.ts +46 -30
  97. package/corpus/templates/assets/actions/generate-video.ts +18 -3
  98. package/corpus/templates/assets/actions/list-assets.ts +39 -8
  99. package/corpus/templates/assets/actions/list-libraries.ts +24 -2
  100. package/corpus/templates/assets/actions/navigate.ts +1 -1
  101. package/corpus/templates/assets/actions/open-asset-picker.ts +38 -1
  102. package/corpus/templates/assets/actions/refresh-generation-run.ts +10 -0
  103. package/corpus/templates/assets/actions/save-generated-image.ts +21 -19
  104. package/corpus/templates/assets/actions/variant-slots.ts +131 -28
  105. package/corpus/templates/assets/actions/view-screen.ts +9 -0
  106. package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +425 -0
  107. package/corpus/templates/assets/app/components/layout/Header.tsx +3 -1
  108. package/corpus/templates/assets/app/components/layout/Layout.tsx +5 -0
  109. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +4 -6
  110. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +47 -21
  111. package/corpus/templates/assets/app/i18n-data.ts +1815 -0
  112. package/corpus/templates/assets/app/lib/libraries.ts +6 -0
  113. package/corpus/templates/assets/app/lib/picker-chat-handoff.ts +71 -0
  114. package/corpus/templates/assets/app/routes/_index.tsx +4 -0
  115. package/corpus/templates/assets/app/routes/asset.$id.tsx +2 -2
  116. package/corpus/templates/assets/app/routes/audit.tsx +1 -1
  117. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +1557 -2164
  118. package/corpus/templates/assets/app/routes/brand-kits._index.tsx +7 -191
  119. package/corpus/templates/assets/app/routes/brand-kits.tsx +1 -5
  120. package/corpus/templates/assets/app/routes/libraries.tsx +4 -4
  121. package/corpus/templates/assets/app/routes/library.$id.tsx +9 -7
  122. package/corpus/templates/assets/app/routes/library.tsx +1454 -175
  123. package/corpus/templates/assets/server/db/index.ts +1 -1
  124. package/corpus/templates/assets/server/lib/generation.ts +86 -15
  125. package/corpus/templates/assets/server/plugins/agent-chat.ts +196 -0
  126. package/corpus/templates/assets/server/plugins/db.ts +5 -0
  127. package/corpus/templates/assets/shared/api.ts +6 -3
  128. package/dist/agent/types.d.ts +21 -0
  129. package/dist/agent/types.d.ts.map +1 -1
  130. package/dist/agent/types.js.map +1 -1
  131. package/dist/client/AgentPanel.d.ts +6 -2
  132. package/dist/client/AgentPanel.d.ts.map +1 -1
  133. package/dist/client/AgentPanel.js +10 -4
  134. package/dist/client/AgentPanel.js.map +1 -1
  135. package/dist/client/AssistantChat.d.ts +6 -0
  136. package/dist/client/AssistantChat.d.ts.map +1 -1
  137. package/dist/client/AssistantChat.js +15 -3
  138. package/dist/client/AssistantChat.js.map +1 -1
  139. package/dist/client/agent-chat.d.ts +32 -0
  140. package/dist/client/agent-chat.d.ts.map +1 -1
  141. package/dist/client/agent-chat.js +113 -0
  142. package/dist/client/agent-chat.js.map +1 -1
  143. package/dist/client/analytics.d.ts +22 -3
  144. package/dist/client/analytics.d.ts.map +1 -1
  145. package/dist/client/analytics.js +96 -4
  146. package/dist/client/analytics.js.map +1 -1
  147. package/dist/client/composer/TiptapComposer.d.ts +2 -1
  148. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  149. package/dist/client/composer/TiptapComposer.js +228 -56
  150. package/dist/client/composer/TiptapComposer.js.map +1 -1
  151. package/dist/client/composer/extensions/MentionReference.d.ts.map +1 -1
  152. package/dist/client/composer/extensions/MentionReference.js +3 -0
  153. package/dist/client/composer/extensions/MentionReference.js.map +1 -1
  154. package/dist/client/composer/types.d.ts +21 -0
  155. package/dist/client/composer/types.d.ts.map +1 -1
  156. package/dist/client/composer/types.js.map +1 -1
  157. package/dist/client/index.d.ts +2 -2
  158. package/dist/client/index.d.ts.map +1 -1
  159. package/dist/client/index.js +2 -2
  160. package/dist/client/index.js.map +1 -1
  161. package/dist/client/session-replay.d.ts +41 -0
  162. package/dist/client/session-replay.d.ts.map +1 -0
  163. package/dist/client/session-replay.js +595 -0
  164. package/dist/client/session-replay.js.map +1 -0
  165. package/dist/collab/awareness.d.ts +2 -2
  166. package/dist/collab/awareness.d.ts.map +1 -1
  167. package/dist/collab/routes.d.ts +2 -2
  168. package/dist/db/schema.d.ts +3 -1
  169. package/dist/db/schema.d.ts.map +1 -1
  170. package/dist/db/schema.js +6 -2
  171. package/dist/db/schema.js.map +1 -1
  172. package/dist/deploy/build.d.ts +8 -0
  173. package/dist/deploy/build.d.ts.map +1 -1
  174. package/dist/deploy/build.js +66 -0
  175. package/dist/deploy/build.js.map +1 -1
  176. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  177. package/dist/notifications/routes.d.ts +3 -3
  178. package/dist/observability/routes.d.ts +8 -8
  179. package/dist/private-blob/index.d.ts +3 -0
  180. package/dist/private-blob/index.d.ts.map +1 -0
  181. package/dist/private-blob/index.js +2 -0
  182. package/dist/private-blob/index.js.map +1 -0
  183. package/dist/private-blob/registry.d.ts +10 -0
  184. package/dist/private-blob/registry.d.ts.map +1 -0
  185. package/dist/private-blob/registry.js +152 -0
  186. package/dist/private-blob/registry.js.map +1 -0
  187. package/dist/private-blob/types.d.ts +51 -0
  188. package/dist/private-blob/types.d.ts.map +1 -0
  189. package/dist/private-blob/types.js +2 -0
  190. package/dist/private-blob/types.js.map +1 -0
  191. package/dist/resources/handlers.d.ts +3 -3
  192. package/dist/scripts/utils.d.ts +4 -4
  193. package/dist/scripts/utils.d.ts.map +1 -1
  194. package/dist/scripts/utils.js +30 -6
  195. package/dist/scripts/utils.js.map +1 -1
  196. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  197. package/dist/server/agent-chat-plugin.js +5 -0
  198. package/dist/server/agent-chat-plugin.js.map +1 -1
  199. package/dist/server/agent-engine-api-key-route.d.ts +2 -2
  200. package/dist/server/email.d.ts +2 -0
  201. package/dist/server/email.d.ts.map +1 -1
  202. package/dist/server/email.js +3 -0
  203. package/dist/server/email.js.map +1 -1
  204. package/dist/server/index.d.ts +1 -1
  205. package/dist/server/index.d.ts.map +1 -1
  206. package/dist/server/index.js.map +1 -1
  207. package/dist/server/transcribe-voice.d.ts +1 -1
  208. package/dist/styles/agent-native.css +24 -0
  209. package/docs/content/template-analytics.md +5 -0
  210. package/docs/content/tracking.md +45 -0
  211. package/package.json +3 -1
  212. package/corpus/templates/analytics/app/pages/overview/OverviewPage.tsx +0 -209
package/corpus/README.md CHANGED
@@ -27,5 +27,5 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
27
27
 
28
28
  ## Generated Counts
29
29
 
30
- - core files: 1933
31
- - template files: 4312
30
+ - core files: 1937
31
+ - template files: 4351
@@ -1,5 +1,25 @@
1
1
  # @agent-native/core
2
2
 
3
+ ## 0.78.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 71849f1: Add structured composer reference chips that can be inserted programmatically
8
+ and constrained to one visible value per app-defined slot.
9
+ - 71849f1: Forward chat thread footer slots through AgentSidebar so apps can keep live run
10
+ results attached to chat threads.
11
+
12
+ ## 0.78.0
13
+
14
+ ### Minor Changes
15
+
16
+ - 368e2a7: Add core session replay client primitives and private blob storage abstractions for Agent Native Analytics.
17
+
18
+ ### Patch Changes
19
+
20
+ - 368e2a7: Support CID inline attachments in the core email transport.
21
+ - 368e2a7: Keep agent sidebar contents at their full open width while desktop sidebars animate in and out, avoiding text reflow during the transition, and hide the page-level New chat action until a chat has actually started.
22
+
3
23
  ## 0.77.24
4
24
 
5
25
  ### Patch Changes
@@ -91,6 +91,11 @@ pnpm install
91
91
  pnpm dev
92
92
  ```
93
93
 
94
+ The Analytics template's dev script pins `DATABASE_URL` to the local SQLite
95
+ file at `./data/app.db`, so a production `DATABASE_URL` in `.env` will not be
96
+ used during local development. To intentionally point local dev at another
97
+ database, run `ANALYTICS_DATABASE_URL=<your dev database url> pnpm dev`.
98
+
94
99
  The CLI prints the local dev URL. Sign in with Google, then open the **Data Sources** page to connect BigQuery, GA4, first-party tracking, HubSpot, Jira, and the rest.
95
100
 
96
101
  ### Key features
@@ -132,6 +132,51 @@ Key differences from the [server `track()`](#track):
132
132
 
133
133
  This is distinct from the framework's internal browser telemetry (`trackEvent()` / automatic pageviews — see [Browser defaults](#browser-defaults) below), which powers Agent Native's own product analytics. Use `track()` for your app's own analytics events that should reach your configured providers.
134
134
 
135
+ ## Session replay {#session-replay}
136
+
137
+ Agent Native apps can opt into first-party browser session replay without adding a second analytics SDK. Call `configureTracking()` once in the browser root and pass the Analytics public key plus collector endpoint:
138
+
139
+ ```ts
140
+ import { configureTracking } from "@agent-native/core/client";
141
+
142
+ configureTracking({
143
+ key: "anpk_...",
144
+ endpoint: "https://analytics.example.com/api/analytics/track",
145
+ sessionReplay: {
146
+ enabled: true,
147
+ sampleRate: 1,
148
+ },
149
+ getDefaultProps: (_event, props) => ({
150
+ ...props,
151
+ app: "my-app",
152
+ template: "my-template",
153
+ }),
154
+ });
155
+ ```
156
+
157
+ When `sessionReplay.enabled` is truthy, the client dynamically imports `@rrweb/record` after startup and posts replay chunks to the replay endpoint. If `endpoint` ends in `/api/analytics/track` or `/track`, the replay endpoint is derived automatically as `/api/analytics/replay`. Override it explicitly with `sessionReplay.endpoint` when the replay collector lives somewhere else.
158
+
159
+ Agent Native template roots already call `configureTracking()`, so hosted template deployments can use Vite/Netlify environment variables instead of editing code:
160
+
161
+ ```bash
162
+ VITE_AGENT_NATIVE_ANALYTICS_PUBLIC_KEY=anpk_...
163
+ VITE_AGENT_NATIVE_ANALYTICS_ENDPOINT=https://analytics.example.com/api/analytics/track
164
+ VITE_AGENT_NATIVE_SESSION_REPLAY_ENABLED=true
165
+ VITE_AGENT_NATIVE_SESSION_REPLAY_SAMPLE_RATE=1
166
+ ```
167
+
168
+ Privacy defaults are intentionally conservative but still useful for playback:
169
+
170
+ - Inputs are masked by default (`maskAllInputs: true`).
171
+ - Page text remains visible unless an element is marked with `.an-mask` or `data-an-mask`.
172
+ - Sensitive zones are blocked with selectors such as `[data-sensitive]`, `.an-block`, `.an-private`, `data-an-block`, `data-an-private`, and credit-card/password/SSN-like fields.
173
+ - URLs are scrubbed with the same `scrubUrl()` helper used by browser analytics.
174
+ - Replay capture is web-only and opt-in; it does not record native desktop screens.
175
+
176
+ The Analytics template stores replay metadata in SQL (`session_recordings`) and stores chunks through private blob refs (`session_replay_chunks`). Browsers and agents never receive provider URLs. Playback goes through scoped server routes and the default agent tools return summaries or bounded replay events, not raw chunk table access.
177
+
178
+ For local development only, Analytics can fall back to capped SQL inline chunks when private blob storage is unavailable. Production deployments should configure private or encrypted blob storage rather than relying on Postgres for replay payloads.
179
+
135
180
  ## Advanced: custom providers & internals {#advanced}
136
181
 
137
182
  Most apps only need `track()` / `identify()` and a built-in provider. The rest of the surface — registering custom providers, the `TrackingProvider` interface, batching internals, and the framework's own browser telemetry — is below.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.77.24",
3
+ "version": "0.78.1",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -101,6 +101,7 @@
101
101
  "./tools/url-safety": "./dist/extensions/url-safety.js",
102
102
  "./file-upload": "./dist/file-upload/index.js",
103
103
  "./file-upload/actions/upload-image": "./dist/file-upload/actions/upload-image.js",
104
+ "./private-blob": "./dist/private-blob/index.js",
104
105
  "./agent/context-xray/actions/context-manifest-get": "./dist/agent/context-xray/actions/context-manifest-get.js",
105
106
  "./agent/context-xray/actions/context-pin": "./dist/agent/context-xray/actions/context-pin.js",
106
107
  "./agent/context-xray/actions/context-evict": "./dist/agent/context-xray/actions/context-evict.js",
@@ -211,6 +212,7 @@
211
212
  "@react-router/dev": "^8.0.1",
212
213
  "@react-router/fs-routes": "^8.0.1",
213
214
  "@resvg/resvg-js": "^2.6.2",
215
+ "@rrweb/record": "2.0.1",
214
216
  "@sentry/browser": "10.60.0",
215
217
  "@sentry/node": "10.60.0",
216
218
  "@standard-schema/spec": "^1.1.0",
@@ -74,6 +74,9 @@ export interface AgentChatReference {
74
74
  source: string;
75
75
  refType?: string;
76
76
  refId?: string;
77
+ slotKey?: string;
78
+ slotLabel?: string;
79
+ metadata?: Record<string, unknown>;
77
80
  }
78
81
 
79
82
  export interface MentionProviderItem {
@@ -84,6 +87,25 @@ export interface MentionProviderItem {
84
87
  refType: string;
85
88
  refId?: string;
86
89
  refPath?: string;
90
+ slotKey?: string;
91
+ slotLabel?: string;
92
+ metadata?: Record<string, unknown>;
93
+ clearsSlots?: string[];
94
+ relatedReferences?: MentionProviderReference[];
95
+ }
96
+
97
+ export interface MentionProviderReference {
98
+ label: string;
99
+ icon?: string;
100
+ source?: string;
101
+ refType: string;
102
+ refId?: string | null;
103
+ refPath?: string | null;
104
+ slotKey?: string;
105
+ slotLabel?: string;
106
+ metadata?: Record<string, unknown>;
107
+ clearsSlots?: string[];
108
+ relatedReferences?: MentionProviderReference[];
87
109
  }
88
110
 
89
111
  export interface MentionProvider {
@@ -179,6 +179,7 @@ const AGENT_PANEL_ROOT_STYLE = {
179
179
  } satisfies React.CSSProperties;
180
180
  type AgentPanelStyle = React.CSSProperties & {
181
181
  "--agent-sidebar-closed-transform"?: string;
182
+ "--agent-sidebar-inner-closed-transform"?: string;
182
183
  "--agent-sidebar-width"?: string;
183
184
  };
184
185
  const AGENT_PANEL_HEADER_CLASS =
@@ -383,11 +384,15 @@ export function shouldShowAgentPanelChatTabBar(
383
384
  }
384
385
 
385
386
  export function shouldShowAgentPanelPageNewChatButton(
386
- _tabs: MultiTabAssistantChatHeaderProps["tabs"],
387
+ tabs: MultiTabAssistantChatHeaderProps["tabs"],
387
388
  activeTabId: string,
388
- _activeTabMessageCount: number,
389
+ activeTabMessageCount: number,
389
390
  ) {
390
- return Boolean(activeTabId);
391
+ if (!activeTabId) return false;
392
+ if (activeTabMessageCount > 0) return true;
393
+
394
+ const activeTab = tabs.find((tab) => tab.id === activeTabId);
395
+ return activeTab?.status === "running" || activeTab?.status === "completed";
391
396
  }
392
397
 
393
398
  export function shouldShowAgentPanelCliTabBar(cliTabs: string[]) {
@@ -2333,6 +2338,10 @@ export interface AgentSidebarProps {
2333
2338
  suggestions?: string[];
2334
2339
  /** Context-aware suggestions merged with `suggestions`. Enabled by default. */
2335
2340
  dynamicSuggestions?: AssistantChatProps["dynamicSuggestions"];
2341
+ /** Optional controls rendered in the chat composer toolbar. */
2342
+ composerToolbarSlot?: AssistantChatProps["composerToolbarSlot"];
2343
+ /** Optional content rendered at the bottom of the chat thread. */
2344
+ threadFooterSlot?: AssistantChatProps["threadFooterSlot"];
2336
2345
  /** Initial sidebar width in pixels. Mount-only; user resize and a saved
2337
2346
  * localStorage value override this. Default: 380 */
2338
2347
  defaultSidebarWidth?: number;
@@ -2379,6 +2388,8 @@ export function AgentSidebar({
2379
2388
  emptyStateText = "How can I help you?",
2380
2389
  suggestions,
2381
2390
  dynamicSuggestions,
2391
+ composerToolbarSlot,
2392
+ threadFooterSlot,
2382
2393
  defaultSidebarWidth,
2383
2394
  sidebarWidth,
2384
2395
  position = "right",
@@ -2849,6 +2860,7 @@ export function AgentSidebar({
2849
2860
  panelStyle = {
2850
2861
  ...AGENT_PANEL_ROOT_STYLE,
2851
2862
  "--agent-sidebar-width": `${width}px`,
2863
+ "--agent-sidebar-inner-closed-transform": `translateX(${isLeft ? "-" : ""}100%)`,
2852
2864
  width: desktopAnimationEnabled ? undefined : width,
2853
2865
  maxHeight: "100vh",
2854
2866
  borderLeft:
@@ -2894,21 +2906,25 @@ export function AgentSidebar({
2894
2906
  inert={sidebarAnimationEnabled && !panelOpen ? true : undefined}
2895
2907
  aria-hidden={sidebarAnimationEnabled && !panelOpen ? true : undefined}
2896
2908
  >
2897
- <AgentPanel
2898
- emptyStateText={emptyStateText}
2899
- suggestions={suggestions}
2900
- dynamicSuggestions={dynamicSuggestions}
2901
- missingApiKeySetupLayout="sidebar"
2902
- onCollapse={() => setOpenPersisted(false)}
2903
- isFullscreen={effectiveFullscreen}
2904
- onToggleFullscreen={
2905
- isMobile ? undefined : (onFullscreenRequest ?? toggleFullscreen)
2906
- }
2907
- storageKey={storageKey}
2908
- scope={scope}
2909
- browserTabId={browserTabId}
2910
- threadUrlSync={threadUrlSync}
2911
- />
2909
+ <div className="agent-sidebar-panel-inner flex min-h-0 flex-1 flex-col">
2910
+ <AgentPanel
2911
+ emptyStateText={emptyStateText}
2912
+ suggestions={suggestions}
2913
+ dynamicSuggestions={dynamicSuggestions}
2914
+ composerToolbarSlot={composerToolbarSlot}
2915
+ threadFooterSlot={threadFooterSlot}
2916
+ missingApiKeySetupLayout="sidebar"
2917
+ onCollapse={() => setOpenPersisted(false)}
2918
+ isFullscreen={effectiveFullscreen}
2919
+ onToggleFullscreen={
2920
+ isMobile ? undefined : (onFullscreenRequest ?? toggleFullscreen)
2921
+ }
2922
+ storageKey={storageKey}
2923
+ scope={scope}
2924
+ browserTabId={browserTabId}
2925
+ threadUrlSync={threadUrlSync}
2926
+ />
2927
+ </div>
2912
2928
  </div>
2913
2929
  {showResizeHandle && isLeft && (
2914
2930
  <ResizeHandle position={position} onDrag={handleDrag} />
@@ -601,6 +601,13 @@ export interface AssistantChatHandle {
601
601
  exportThreadSnapshot(): ChatThreadSnapshot | null;
602
602
  }
603
603
 
604
+ export type AssistantChatThreadFooterSlot =
605
+ | React.ReactNode
606
+ | ((context: {
607
+ threadId: string | null;
608
+ tabId: string | null;
609
+ }) => React.ReactNode);
610
+
604
611
  export interface AssistantChatAdapterContext {
605
612
  apiUrl: string;
606
613
  tabId?: string;
@@ -638,6 +645,8 @@ export interface AssistantChatProps {
638
645
  suggestions?: string[];
639
646
  /** Context-aware suggestions merged with `suggestions`. Enabled by default. */
640
647
  dynamicSuggestions?: AgentDynamicSuggestionsOption;
648
+ /** Optional content rendered at the bottom of the scrollable thread, after messages. */
649
+ threadFooterSlot?: AssistantChatThreadFooterSlot;
641
650
  /** Optional content rendered in the empty state, above the suggestion buttons. */
642
651
  emptyStateAddon?: React.ReactNode;
643
652
  /** Whether to show the header bar. Default: true */
@@ -952,6 +961,7 @@ const AssistantChatInner = forwardRef<
952
961
  emptyStateText,
953
962
  suggestions,
954
963
  dynamicSuggestions,
964
+ threadFooterSlot,
955
965
  emptyStateAddon,
956
966
  showHeader = true,
957
967
  onSwitchToCli,
@@ -2870,6 +2880,14 @@ const AssistantChatInner = forwardRef<
2870
2880
  isAutoResuming ||
2871
2881
  queuedMessages.length > 0 ||
2872
2882
  reconnectContent.length > 0;
2883
+ const resolvedThreadFooterSlot =
2884
+ typeof threadFooterSlot === "function"
2885
+ ? threadFooterSlot({
2886
+ threadId: threadId ?? null,
2887
+ tabId: tabId ?? null,
2888
+ })
2889
+ : threadFooterSlot;
2890
+ const hasThreadFooterSlot = Boolean(resolvedThreadFooterSlot);
2873
2891
  const isFreshEmptyChat =
2874
2892
  messages.length === 0 &&
2875
2893
  !hasActiveChatWork &&
@@ -2887,6 +2905,13 @@ const AssistantChatInner = forwardRef<
2887
2905
  centerComposerWhenEmpty && (isFreshEmptyChat || centeredRestoringState);
2888
2906
  const showEmptyState =
2889
2907
  messages.length === 0 && !isReconnecting && !hasActiveChatWork;
2908
+ const showInlineEmptyThreadFooterSlot =
2909
+ showEmptyState &&
2910
+ !centeredEmptyState &&
2911
+ !isRestoring &&
2912
+ hasThreadFooterSlot;
2913
+ const showCenteredEmptyThreadFooterSlot =
2914
+ centeredEmptyState && !isRestoring && hasThreadFooterSlot;
2890
2915
  const showComposerSlot =
2891
2916
  Boolean(composerSlot) && (!centerComposerWhenEmpty || centeredEmptyState);
2892
2917
 
@@ -3139,6 +3164,11 @@ const AssistantChatInner = forwardRef<
3139
3164
  ))}
3140
3165
  </div>
3141
3166
  ) : null}
3167
+ {showInlineEmptyThreadFooterSlot ? (
3168
+ <div className="agent-thread-footer-slot agent-thread-footer-slot--empty">
3169
+ {resolvedThreadFooterSlot}
3170
+ </div>
3171
+ ) : null}
3142
3172
  </div>
3143
3173
  ) : (
3144
3174
  <div className="agent-thread-content flex flex-col gap-4 px-4 py-4">
@@ -3266,6 +3296,11 @@ const AssistantChatInner = forwardRef<
3266
3296
  </div>
3267
3297
  );
3268
3298
  })}
3299
+ {resolvedThreadFooterSlot ? (
3300
+ <div className="agent-thread-footer-slot">
3301
+ {resolvedThreadFooterSlot}
3302
+ </div>
3303
+ ) : null}
3269
3304
  </div>
3270
3305
  )}
3271
3306
  </div>
@@ -3285,6 +3320,11 @@ const AssistantChatInner = forwardRef<
3285
3320
  )}
3286
3321
 
3287
3322
  {showComposerSlot ? composerSlot : null}
3323
+ {showCenteredEmptyThreadFooterSlot ? (
3324
+ <div className="agent-thread-footer-slot agent-thread-footer-slot--centered-empty">
3325
+ {resolvedThreadFooterSlot}
3326
+ </div>
3327
+ ) : null}
3288
3328
  {guidedQuestions && guidedQuestions.length > 0 && (
3289
3329
  <div className="shrink-0 px-3 pb-2 pt-1">
3290
3330
  <div className="rounded-lg border border-border bg-card/60 shadow-sm">
@@ -117,6 +117,37 @@ export interface AgentChatContextState {
117
117
  updatedAt: number;
118
118
  }
119
119
 
120
+ export interface AgentComposerReference {
121
+ label: string;
122
+ icon?: string;
123
+ source?: string;
124
+ refType: string;
125
+ refId?: string | null;
126
+ refPath?: string | null;
127
+ /** Stable composer slot this reference occupies. Slot references replace older values. */
128
+ slotKey?: string;
129
+ /** Short label shown before the selected value in the composer chip. */
130
+ slotLabel?: string;
131
+ /** Additional app-defined data used by the client for filtering and grouping. */
132
+ metadata?: Record<string, unknown>;
133
+ /** Slots to remove when this reference is inserted or removed. */
134
+ clearsSlots?: string[];
135
+ /** Additional references to insert before this one. */
136
+ relatedReferences?: AgentComposerReference[];
137
+ }
138
+
139
+ export interface AgentComposerReferenceInsertOptions {
140
+ /**
141
+ * Whether to open the agent sidebar before inserting the reference.
142
+ * Defaults to false so contextual auto-tags can stay quiet.
143
+ */
144
+ openSidebar?: boolean;
145
+ }
146
+
147
+ export interface AgentComposerReferenceInsertPayload extends AgentComposerReference {
148
+ insertMessageId: string;
149
+ }
150
+
120
151
  export interface AgentChatContextMutationOptions {
121
152
  /**
122
153
  * Whether to open the agent sidebar if it's currently hidden.
@@ -140,6 +171,10 @@ export const AGENT_CHAT_REMOVE_CONTEXT_MESSAGE_TYPE =
140
171
  "agentNative.removeChatContext";
141
172
  export const AGENT_CHAT_CLEAR_CONTEXT_MESSAGE_TYPE =
142
173
  "agentNative.clearChatContext";
174
+ export const AGENT_CHAT_INSERT_REFERENCE_MESSAGE_TYPE =
175
+ "agentNative.insertComposerReference";
176
+ export const AGENT_CHAT_INSERT_REFERENCE_EVENT =
177
+ "agentNative:insert-composer-reference";
143
178
  const AGENT_PANEL_PREPARE_EVENT = "agent-panel:prepare";
144
179
 
145
180
  let agentChatContextState: AgentChatContextState = {
@@ -406,6 +441,82 @@ export function appendAgentChatContextToMessage(
406
441
  return `${message.trim()}\n\n<context>\n${trimmedContext}\n</context>`;
407
442
  }
408
443
 
444
+ function normalizeStringArray(value: unknown): string[] | undefined {
445
+ if (!Array.isArray(value)) return undefined;
446
+ const normalized = value
447
+ .filter((item): item is string => typeof item === "string")
448
+ .map((item) => item.trim())
449
+ .filter(Boolean);
450
+ return normalized.length > 0 ? normalized : undefined;
451
+ }
452
+
453
+ function normalizeMetadata(
454
+ value: unknown,
455
+ ): Record<string, unknown> | undefined {
456
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
457
+ return undefined;
458
+ }
459
+ return value as Record<string, unknown>;
460
+ }
461
+
462
+ function normalizeAgentComposerReferenceInternal(
463
+ value: unknown,
464
+ depth: number,
465
+ ): AgentComposerReference | null {
466
+ if (typeof value !== "object" || value === null) return null;
467
+ const candidate = value as Partial<AgentComposerReference>;
468
+ const label =
469
+ typeof candidate.label === "string" ? candidate.label.trim() : "";
470
+ const refType =
471
+ typeof candidate.refType === "string" ? candidate.refType.trim() : "";
472
+ if (!label || !refType) return null;
473
+ const normalized: AgentComposerReference = {
474
+ label,
475
+ icon:
476
+ typeof candidate.icon === "string" && candidate.icon.trim()
477
+ ? candidate.icon.trim()
478
+ : undefined,
479
+ source:
480
+ typeof candidate.source === "string" && candidate.source.trim()
481
+ ? candidate.source.trim()
482
+ : undefined,
483
+ refType,
484
+ refId:
485
+ typeof candidate.refId === "string" && candidate.refId.trim()
486
+ ? candidate.refId.trim()
487
+ : null,
488
+ refPath:
489
+ typeof candidate.refPath === "string" && candidate.refPath.trim()
490
+ ? candidate.refPath.trim()
491
+ : null,
492
+ };
493
+ const slotKey =
494
+ typeof candidate.slotKey === "string" ? candidate.slotKey.trim() : "";
495
+ if (slotKey) normalized.slotKey = slotKey;
496
+ const slotLabel =
497
+ typeof candidate.slotLabel === "string" ? candidate.slotLabel.trim() : "";
498
+ if (slotLabel) normalized.slotLabel = slotLabel;
499
+ const metadata = normalizeMetadata(candidate.metadata);
500
+ if (metadata) normalized.metadata = metadata;
501
+ const clearsSlots = normalizeStringArray(candidate.clearsSlots);
502
+ if (clearsSlots) normalized.clearsSlots = clearsSlots;
503
+ if (depth < 3 && Array.isArray(candidate.relatedReferences)) {
504
+ const relatedReferences = candidate.relatedReferences
505
+ .map((item) => normalizeAgentComposerReferenceInternal(item, depth + 1))
506
+ .filter((item): item is AgentComposerReference => item !== null);
507
+ if (relatedReferences.length > 0) {
508
+ normalized.relatedReferences = relatedReferences;
509
+ }
510
+ }
511
+ return normalized;
512
+ }
513
+
514
+ export function normalizeAgentComposerReference(
515
+ value: unknown,
516
+ ): AgentComposerReference | null {
517
+ return normalizeAgentComposerReferenceInternal(value, 0);
518
+ }
519
+
409
520
  function postAgentChatContextMessage(
410
521
  type:
411
522
  | typeof AGENT_CHAT_SET_CONTEXT_MESSAGE_TYPE
@@ -446,6 +557,51 @@ function postAgentChatContextMessage(
446
557
  }
447
558
  }
448
559
 
560
+ function postAgentChatReferenceMessage(
561
+ payload: AgentComposerReferenceInsertPayload,
562
+ options: { openSidebar: boolean },
563
+ ): void {
564
+ if (typeof window === "undefined") return;
565
+
566
+ const message = {
567
+ type: AGENT_CHAT_INSERT_REFERENCE_MESSAGE_TYPE,
568
+ data: payload,
569
+ };
570
+ const targetSelf = isInBuilderFrame() || isDirectMcpAppEmbedSession();
571
+ const target = targetSelf
572
+ ? window
573
+ : window.parent !== window
574
+ ? window.parent
575
+ : window;
576
+ const targetOrigin = targetSelf
577
+ ? window.location.origin
578
+ : getFramePostMessageTargetOrigin() || window.location.origin;
579
+
580
+ if (options.openSidebar) {
581
+ window.dispatchEvent(
582
+ new CustomEvent("agent-panel:set-mode", {
583
+ detail: { mode: "chat" },
584
+ }),
585
+ );
586
+ window.dispatchEvent(new CustomEvent("agent-panel:open"));
587
+ } else {
588
+ window.dispatchEvent(new CustomEvent(AGENT_PANEL_PREPARE_EVENT));
589
+ }
590
+
591
+ window.dispatchEvent(
592
+ new CustomEvent(AGENT_CHAT_INSERT_REFERENCE_EVENT, {
593
+ detail: payload,
594
+ }),
595
+ );
596
+
597
+ const postToTarget = () => target.postMessage(message, targetOrigin);
598
+ if (target === window) {
599
+ setTimeout(postToTarget, 0);
600
+ } else {
601
+ postToTarget();
602
+ }
603
+ }
604
+
449
605
  function isMcpAppChatBridgeEnabled(): boolean {
450
606
  if (typeof window === "undefined" || window.parent === window) return false;
451
607
  if (readEmbedMcpChatBridgeFlagFromUrl()) markEmbedMcpChatBridgeActive();
@@ -685,6 +841,23 @@ export const setContextToAgentChat = setAgentChatContextItem;
685
841
  /** @deprecated Use `setAgentChatContextItem` instead. */
686
842
  export const addContextToAgentChat = setAgentChatContextItem;
687
843
 
844
+ export function insertAgentComposerReference(
845
+ ref: AgentComposerReference,
846
+ options: AgentComposerReferenceInsertOptions = {},
847
+ ): void {
848
+ const normalized = normalizeAgentComposerReference(ref);
849
+ if (!normalized || typeof window === "undefined") return;
850
+ postAgentChatReferenceMessage(
851
+ {
852
+ ...normalized,
853
+ insertMessageId: `reference-${Date.now()}-${Math.random()
854
+ .toString(36)
855
+ .slice(2, 8)}`,
856
+ },
857
+ { openSidebar: options.openSidebar === true },
858
+ );
859
+ }
860
+
688
861
  export function removeAgentChatContextItem(
689
862
  keyOrOpts: string | AgentChatContextRemoveOptions,
690
863
  ): void {