@agent-native/core 0.216.0 → 0.218.0

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 (65) hide show
  1. package/dist/app-config/analytics.js +1 -1
  2. package/dist/client/agent-chat/index.d.ts +1 -1
  3. package/dist/client/analytics-session.d.ts +7 -0
  4. package/dist/client/analytics-session.js +27 -1
  5. package/dist/client/analytics.js +110 -15
  6. package/dist/client/consent.d.ts +12 -0
  7. package/dist/client/consent.js +132 -2
  8. package/dist/client/onboarding/index.d.ts +1 -0
  9. package/dist/client/onboarding/index.js +1 -0
  10. package/dist/client/onboarding/use-onboarding-checklist.d.ts +23 -0
  11. package/dist/client/onboarding/use-onboarding-checklist.js +115 -0
  12. package/dist/client/session-replay.d.ts +1 -1
  13. package/dist/client/session-replay.js +142 -3
  14. package/dist/client/use-chat-thread-list.d.ts +3 -0
  15. package/dist/client/use-chat-thread-list.js +23 -7
  16. package/dist/collab/routes.d.ts +1 -1
  17. package/dist/localization/consent-banner-messages.d.ts +16 -0
  18. package/dist/localization/consent-banner-messages.js +149 -0
  19. package/dist/localization/core-messages.js +4 -0
  20. package/dist/localization/default-messages.d.ts +1 -0
  21. package/dist/localization/default-messages.js +2 -0
  22. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  23. package/dist/notifications/routes.d.ts +1 -1
  24. package/dist/observability/routes.d.ts +7 -7
  25. package/dist/onboarding/index.d.ts +2 -1
  26. package/dist/onboarding/index.js +1 -0
  27. package/dist/onboarding/plugin.js +55 -46
  28. package/dist/onboarding/status.d.ts +15 -0
  29. package/dist/onboarding/status.js +59 -0
  30. package/dist/onboarding/types.d.ts +12 -0
  31. package/dist/onboarding/types.js +2 -1
  32. package/dist/org/workspace-app-access.js +19 -9
  33. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  34. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  35. package/dist/resources/handlers.d.ts +1 -1
  36. package/dist/secrets/routes.d.ts +10 -10
  37. package/dist/server/agent-capabilities.js +13 -6
  38. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  39. package/dist/server/analytics.d.ts +0 -1
  40. package/dist/server/analytics.js +81 -20
  41. package/dist/server/builder-api-auth.d.ts +7 -0
  42. package/dist/server/builder-api-auth.js +24 -0
  43. package/dist/server/builder-oauth.d.ts +5 -1
  44. package/dist/server/builder-oauth.js +5 -0
  45. package/dist/server/first-touch-route.d.ts +2 -2
  46. package/dist/server/index.d.ts +1 -1
  47. package/dist/server/index.js +1 -1
  48. package/dist/server/realtime-token.d.ts +1 -1
  49. package/dist/shared/consent.d.ts +2 -0
  50. package/dist/shared/consent.js +4 -0
  51. package/dist/templates/builder-code-starter/template-layer.json +1 -0
  52. package/dist/templates/chat/app/components/layout/Sidebar.tsx +62 -101
  53. package/dist/triggers/actions/manage-automation.d.ts +1 -1
  54. package/dist/usage/actions/get-usage-metrics.js +3 -3
  55. package/docs/content/components.mdx +19 -18
  56. package/docs/content/deployment-environment-variables.mdx +1 -1
  57. package/docs/content/environment-variables.mdx +1 -1
  58. package/docs/content/sharing.mdx +4 -0
  59. package/docs/content/toolkit-sharing.mdx +31 -51
  60. package/migration-manifest.json +1 -1
  61. package/package.json +5 -3
  62. package/src/templates/builder-code-starter/template-layer.json +1 -0
  63. package/src/templates/chat/app/components/layout/Sidebar.tsx +62 -101
  64. package/dist/templates/chat/app/lib/sidebar-thread-state.ts +0 -29
  65. package/src/templates/chat/app/lib/sidebar-thread-state.ts +0 -29
@@ -17,7 +17,7 @@ function resolveAppSelection(app, appId) {
17
17
  return app;
18
18
  }
19
19
  export default defineAction({
20
- description: 'Reports this workspace\'s own agent/LLM usage (Settings › Usage) for members of this organization only; it does NOT cover usage, credits, or activity of external customers, accounts, companies, or product usage, which belong to the app that owns that data (see <relevant-apps>, or search with describe-workspace-apps). Returns lookback totals, the daily trend (dailyBy feature, app, model and surface), top features, models, apps, chats and people, tool calls per day, and recent prompts, across every app unless app names one. Members see only their own usage; scope "workspace" (everyone in the organization) is for owners and admins. billing.unit identifies Builder.io credits, estimated dollars, mixed billing, or unknown.',
20
+ description: "Reports this workspace's own agent/LLM usage (Settings › Usage) for members of this organization only; it does NOT cover usage, credits, or activity of external customers, accounts, companies, or product usage, use this app's own customer or billing actions for those, or the app that owns that data (see <relevant-apps>, or search with describe-workspace-apps). Returns lookback totals, the daily trend (dailyBy feature, app, model and surface), top features, models, apps, chats and people, tool calls per day, and recent prompts, across every app unless app names one. Members see only their own usage; scope \"workspace\" (everyone in the organization) is for owners and admins. billing.unit identifies Builder.io credits, estimated dollars, mixed billing, or unknown.",
21
21
  http: { method: "GET" },
22
22
  schema: z.object({
23
23
  sinceDays: z.coerce.number().int().min(1).max(365).default(30),
@@ -56,10 +56,10 @@ export default defineAction({
56
56
  ownerEmail: ctx.userEmail,
57
57
  orgId: ctx.orgId,
58
58
  }))) {
59
- fail("Only organization owners and admins can look up another person's usage with get-usage-metrics. Usage, credits, or activity for customers, accounts, or companies belongs to the app that owns that data (see <relevant-apps> or run describe-workspace-apps with a query).", { errorCode: "usage_user_lookup_forbidden", statusCode: 403 });
59
+ fail(`get-usage-metrics cannot answer this. For a teammate in this organization, only owners and admins can look up their usage. For a customer or other external user, get-usage-metrics has no data: use this app's own customer or billing actions if it has any, or find the app that owns that data with describe-workspace-apps query="<what you need>" and delegate with call-agent.`, { errorCode: "usage_user_lookup_forbidden", statusCode: 403 });
60
60
  }
61
61
  if (!(await isOrgMember(ctx.orgId, userEmail))) {
62
- fail(`${userEmail} is not a member of this organization. get-usage-metrics only covers this workspace's own agent usage; usage, credits, or activity for customers, accounts, or companies belongs to the app that owns that data (see <relevant-apps> or run describe-workspace-apps with a query).`, { errorCode: "usage_user_not_org_member", statusCode: 404 });
62
+ fail(`${userEmail} is not a member of this organization, so get-usage-metrics has no data on them. For a customer's or account's usage or credits, use this app's own customer or billing actions if it has any, or find the app that owns that data with describe-workspace-apps query="<what you need>" and delegate with call-agent.`, { errorCode: "usage_user_not_org_member", statusCode: 404 });
63
63
  }
64
64
  }
65
65
  const selection = resolveAppSelection(app, appId);
@@ -696,24 +696,25 @@ function WorkspaceResources() {
696
696
 
697
697
  ## Other Public UI {#other-ui}
698
698
 
699
- | Area | APIs | Import path |
700
- | -------------- | ------------------------------------------------------------------------------ | ------------------------------------------ |
701
- | Agent page | `<AgentTabsPage>` | `@agent-native/toolkit/app/agent-page` |
702
- | Blocks UI | `BlockView`, `SchemaBlockEditor`, `BlockRegistryProvider` | `@agent-native/toolkit/app/blocks` |
703
- | Canvas | `DrawOverlay`, `CanvasCommentPins` | `@agent-native/toolkit/canvas-annotations` |
704
- | Chat history | `ChatHistoryList`, `ChatHistoryRail` | `@agent-native/toolkit/chat-history` |
705
- | Data grid | `<DataGrid>` | `@agent-native/toolkit/data-grid` |
706
- | Dashboard | `DataTable`, `DateRangePicker`, `GenericChartPanel`, `MetricCard`, `StatsCard` | `@agent-native/toolkit/dashboard` |
707
- | Sharing | `<ShareButton>`, `<ShareDialog>` | `@agent-native/toolkit/app/sharing` |
708
- | Sharing UI | `<VisibilityBadge>` | `@agent-native/toolkit/sharing` |
709
- | Notifications | `<NotificationsBell>` | `@agent-native/toolkit/app/notifications` |
710
- | Progress | `<RunsTray>` | `@agent-native/toolkit/app/progress` |
711
- | Run registry | Run types and server registry functions | `@agent-native/core/progress` |
712
- | Onboarding | `useOnboarding()`, onboarding panel hooks | `@agent-native/core/client/onboarding` |
713
- | Observability | `<ObservabilityDashboard>`, `<ThumbsFeedback>` | `@agent-native/toolkit/app/observability` |
714
- | Resources UI | `<ResourcesPanel>`, `<ResourceTree>`, `<ResourceEditor>` | `@agent-native/toolkit/app/resources` |
715
- | Resource hooks | Resource data hooks | `@agent-native/core/client/resources` |
716
- | Rich editor | `<SharedRichEditor>`, slash commands, block node hooks | `@agent-native/toolkit/editor` |
699
+ | Area | APIs | Import path |
700
+ | --------------- | ------------------------------------------------------------------------------ | -------------------------------------------------- |
701
+ | Agent page | `<AgentTabsPage>` | `@agent-native/toolkit/app/agent-page` |
702
+ | Blocks UI | `BlockView`, `SchemaBlockEditor`, `BlockRegistryProvider` | `@agent-native/toolkit/app/blocks` |
703
+ | Canvas | `DrawOverlay`, `CanvasCommentPins` | `@agent-native/toolkit/canvas-annotations` |
704
+ | Chat history | `ChatHistoryList`, `ChatHistoryRail` | `@agent-native/toolkit/chat-history` |
705
+ | Owned chat rail | `useOwnershipChatHistory()`, `OwnershipChatHistoryRail` | `@agent-native/toolkit/app/chat/ownership-history` |
706
+ | Data grid | `<DataGrid>` | `@agent-native/toolkit/data-grid` |
707
+ | Dashboard | `DataTable`, `DateRangePicker`, `GenericChartPanel`, `MetricCard`, `StatsCard` | `@agent-native/toolkit/dashboard` |
708
+ | Sharing | `<ShareButton>`, `<ShareDialog>` | `@agent-native/toolkit/app/sharing` |
709
+ | Sharing UI | `<VisibilityBadge>` | `@agent-native/toolkit/sharing` |
710
+ | Notifications | `<NotificationsBell>` | `@agent-native/toolkit/app/notifications` |
711
+ | Progress | `<RunsTray>` | `@agent-native/toolkit/app/progress` |
712
+ | Run registry | Run types and server registry functions | `@agent-native/core/progress` |
713
+ | Onboarding | `useOnboarding()`, onboarding panel hooks | `@agent-native/core/client/onboarding` |
714
+ | Observability | `<ObservabilityDashboard>`, `<ThumbsFeedback>` | `@agent-native/toolkit/app/observability` |
715
+ | Resources UI | `<ResourcesPanel>`, `<ResourceTree>`, `<ResourceEditor>` | `@agent-native/toolkit/app/resources` |
716
+ | Resource hooks | Resource data hooks | `@agent-native/core/client/resources` |
717
+ | Rich editor | `<SharedRichEditor>`, slash commands, block node hooks | `@agent-native/toolkit/editor` |
717
718
 
718
719
  ## One-Off Text Completion {#one-off-text-completion}
719
720
 
@@ -20,7 +20,7 @@ hosting behavior.
20
20
  | `APP_URL` | Optional canonical public origin. Checked ahead of `BETTER_AUTH_URL` when resolving the A2A JWT audience, the OAuth public origin, self-dispatch webhook targets, and generated resource links. Set it explicitly on preview/staging deploys where the request host is unreliable. |
21
21
  | `AGENT_PROD_CODE_EXECUTION` | Optional production code-execution mode: `off` (default), `sandboxed`, or `trusted`. See [Production Code Execution](/docs/actions-agent-tools#production-code-execution). |
22
22
  | `AGENT_NATIVE_SSR_CACHE` | Deployment-wide SSR shell cache policy: unset/`on` keeps the default hard-cache, `off` sends `no-store`, or a duration such as `30s` / `5m` sets a shorter freshness. See [SSR Caching](/docs/ssr-caching). |
23
- | `AGENT_NATIVE_HEAD_SCRIPT_SRC` | Optional HTTPS URL of a script loaded with `defer` in the `<head>` of every server-rendered page. Use it for deployment-specific tooling such as an internal feedback widget, without changing app source. Non-HTTPS URLs fail config validation. |
23
+ | `AGENT_NATIVE_HEAD_SCRIPT_SRC` | Optional HTTPS URL of a script added to the `<head>` of every server-rendered page. Where the consent banner shows, it waits for analytics consent, like Google Analytics. Use it for deployment-specific tooling such as an internal feedback widget, without changing app source. Non-HTTPS URLs fail config validation. |
24
24
  | `AGENT_NATIVE_ENABLE_KEEP_WARM` | Opt into the Netlify keep-warm scheduled function. Unset by default. See [Keep-warm](/docs/netlify#netlify-keep-warm). |
25
25
  | `AGENT_NATIVE_DISABLE_KEEP_WARM` | Compatibility kill switch for the Netlify keep-warm scheduled function. See [Keep-warm](/docs/netlify#netlify-keep-warm). |
26
26
  | `AGENT_NATIVE_KEEP_WARM_SCHEDULE` | Override the keep-warm cadence with a 5-field cron expression (default `* * * * *` when enabled). An unparseable value fails the build. See [Keep-warm](/docs/netlify#netlify-keep-warm). |
@@ -95,7 +95,7 @@ public app behavior, use the config paths above instead.
95
95
  | `ACCESS_TOKEN` / `ACCESS_TOKENS` | Static bearer fallback for MCP and Connect clients. | One token or a delimited token list; these are not browser auth. |
96
96
  | `AGENT_PROD_CODE_EXECUTION` | Production code-execution policy. | `off`, `sandboxed`, or `trusted`. See [Production Code Execution](/docs/actions-agent-tools#production-code-execution). |
97
97
  | `AGENT_NATIVE_SSR_CACHE` | Deployment-wide public SSR shell cache policy. | Unset/`on`, `off`, or a duration such as `30s` or `5m`. See [SSR Caching](/docs/ssr-caching). |
98
- | `AGENT_NATIVE_HEAD_SCRIPT_SRC` | Extra script injected into every server-rendered page head with `defer`. | An absolute `https://` URL; non-HTTPS values fail config validation. |
98
+ | `AGENT_NATIVE_HEAD_SCRIPT_SRC` | Extra script added to every server-rendered page head. Where the consent banner shows, it waits for analytics consent, like Google Analytics. | An absolute `https://` URL; non-HTTPS values fail config validation. |
99
99
  | `AGENT_NATIVE_ENABLE_KEEP_WARM` | Opts into the Netlify keep-warm scheduled function; unset by default. | `1`; see [Keep-warm](/docs/netlify#netlify-keep-warm). |
100
100
  | `AGENT_NATIVE_DISABLE_KEEP_WARM` | Compatibility kill switch for the Netlify keep-warm scheduled function. | `1`; see [Keep-warm](/docs/netlify#netlify-keep-warm). |
101
101
  | `AGENT_NATIVE_KEEP_WARM_SCHEDULE` | Keep-warm cadence override when enabled. | A 5-field cron expression such as `*/5 * * * *`; an unparseable value fails the build. |
@@ -194,6 +194,10 @@ const shared = useChatThreadList({ ownership: "shared", pageSize: 50 });
194
194
 
195
195
  A failed request sets `error` and keeps the rows already loaded. It never looks like an empty list. `<ShareButton>` and `<ShareDialog>` refresh mounted chat lists after they save. If you change a chat's sharing some other way, call `notifyChatThreadsUpdated()` so mounted lists refetch.
196
196
 
197
+ For a ready-made sidebar that combines owned chats, shared chats, and eligible started drafts, import `useOwnershipChatHistory()` and `OwnershipChatHistoryRail` from `@agent-native/toolkit/app/chat/ownership-history`. The adapter keeps owner and access-basis details on shared rows, pages each section independently, and retains rows and local optimistic changes when a refresh fails. Its mutation callbacks return `Promise<boolean>`, allowing the app to navigate only after a write succeeds. See [Sharing Kit](/docs/toolkit-sharing#shared-chat-history) for an example.
198
+
199
+ An active empty draft remains selectable and runnable while its first save is pending. Until persistence is confirmed, hide pin, rename, and archive actions, and the adapter rejects calls to those callbacks. `ownDraftRows` supplies persistence provenance for owned drafts, while confirmed owned rows from the server list keep their normal actions. For custom `OwnDraftRow` values, set optional `persisted?: boolean` to `true` only for a confirmed persisted server row owned by the current user. Do not infer persistence from message count or active ID. Role, privacy, and whether the active draft is empty remain separate facts.
200
+
197
201
  ## Same model, agent and UI {#agent-and-ui}
198
202
 
199
203
  The framework auto-mounts these actions in every template — the agent calls them as tools, and the UI calls them through `useActionQuery` / `useActionMutation`:
@@ -138,66 +138,46 @@ tokens that back agent-readable public surfaces.
138
138
 
139
139
  ## Shared chat history {#shared-chat-history}
140
140
 
141
- `@agent-native/toolkit/chat-history` renders the chat sidebar. Core supplies the
142
- rows and Toolkit renders them, so an app can show My chats and Shared with you
143
- as separate sections. See [Sharing & Privacy](/docs/sharing#shared-chats) for
144
- the `ownership` filter and the `ownerEmail` and `accessBasis` fields.
145
-
146
- `<ChatHistoryRail>` accepts `sections` as well as a flat `items` list. Each
147
- section previews five rows and has its own **Show all** and **Show less**
148
- control, loading, error, and empty states, and optional load-more:
141
+ `@agent-native/toolkit/chat-history` provides the presentational rail. For an app sidebar backed by Core chat ownership and lifecycle behavior, use the focused adapter at `@agent-native/toolkit/app/chat/ownership-history`:
149
142
 
150
143
  ```tsx
151
- import { ChatHistoryRail } from "@agent-native/toolkit/chat-history";
152
- import { useChatThreadList } from "@agent-native/core/client";
153
-
154
- const mine = useChatThreadList({ ownership: "mine" });
155
- const shared = useChatThreadList({ ownership: "shared" });
144
+ import {
145
+ ownDraftRows,
146
+ OwnershipChatHistoryRail,
147
+ useOwnershipChatHistory,
148
+ } from "@agent-native/toolkit/app/chat/ownership-history";
149
+ import { useChatThreads } from "@agent-native/core/client/agent-chat";
150
+
151
+ const { threads, isNewThread, pinThread, renameThread, archiveThread } =
152
+ useChatThreads(undefined, CHAT_STORAGE_KEY, null, { autoCreate: false });
153
+
154
+ const history = useOwnershipChatHistory({
155
+ apiUrl,
156
+ scope: { type: "workspace", id: workspaceId },
157
+ activeId: activeThreadId,
158
+ pinned: true,
159
+ localMineRows: ownDraftRows({ threads, isNewThread }),
160
+ mutations: {
161
+ pin: pinThread,
162
+ rename: renameThread,
163
+ archive: archiveThread,
164
+ },
165
+ });
156
166
 
157
- <ChatHistoryRail
167
+ <OwnershipChatHistoryRail
168
+ {...history}
169
+ activeId={activeThreadId}
158
170
  onNewChat={startChat}
159
- railLabels={{
160
- newChat: t("newChat"),
161
- showMore: t("showMore"),
162
- showLess: t("showLess"),
163
- sections: {
164
- showAll: t("showAll"),
165
- showLess: t("showLess"),
166
- loadMore: t("loadMore"),
167
- loadingMore: t("loadingMore"),
168
- retry: t("retry"),
169
- },
170
- }}
171
- sections={[
172
- { id: "mine", label: t("myChats"), items: toItems(mine.threads) },
173
- {
174
- id: "shared",
175
- label: t("sharedWithYou"),
176
- items: toItems(shared.threads),
177
- hasMore: shared.hasMore,
178
- loadingMore: shared.loadingMore,
179
- onLoadMore: shared.loadMore,
180
- loading: shared.loading,
181
- error: shared.error ? t("loadFailed") : undefined,
182
- onRetry: shared.refresh,
183
- },
184
- ]}
171
+ onSelect={selectThread}
172
+ showNewChat={false}
185
173
  />;
186
174
  ```
187
175
 
188
- With `sections`, `railLabels.sections` is required and has no defaults. Pass
189
- `showAll`, `showLess`, `loadMore`, `loadingMore`, and `retry` as translated text,
190
- and override them per section with `section.labels` when needed.
176
+ `useOwnershipChatHistory()` requests owned and shared rows from Core before paging. It keeps owner and access basis on shared rows. When pinned grouping is enabled with `pinned: true`, it separates pinned chats from the viewer's other owned chats. Started drafts are not inferred automatically. If the app has local lifecycle data, pass `localMineRows: ownDraftRows({ threads, isNewThread })` to include started drafts proven to belong to the current user. The rail previews five rows in each section, supports Show all and independent paging, and keeps loaded rows visible when a section request fails. Retry preserves local changes until a successful authoritative refresh can reconcile them.
191
177
 
192
- Show all reveals the rows already loaded. When the server has more, a disclosed
193
- section also offers Load more. A failed load shows above the rows that already
194
- loaded, so it never reads as an empty section.
178
+ The adapter supplies localized section labels. `railLabels` may override `newChat`, `showMore`, or `showLess` individually. `onNewChat` and `onSelect` are required. Pass `activeId` to both the hook and rail. Mutation callbacks return `Promise<boolean>` so failed writes can roll back without navigating away from the active chat.
195
179
 
196
- Rows can show who owns a chat and why the viewer can see it. Set `ownerLabel`,
197
- `accessLabel`, and `accessBasis` on a `ChatHistoryItem`. The app passes
198
- translated text, because the Toolkit list ships none. Shared rows should show a
199
- direct grant over organization access when both apply, which is how Core
200
- reports `accessBasis`.
180
+ For custom layouts, use `chatThreadToHistoryItem()` to convert a Core row and `ownDraftRows()` to derive eligible started drafts. `ChatHistoryRail` remains available when an app already owns its data and lifecycle logic. See [Sharing & Privacy](/docs/sharing#shared-chats) for ownership filters and access fields.
201
181
 
202
182
  ## What's next
203
183
 
@@ -3330,7 +3330,7 @@
3330
3330
  "to": "@agent-native/core/client/sharing",
3331
3331
  "symbols": {
3332
3332
  "VisibilityBadge": {
3333
- "to": "@agent-native/toolkit/sharing"
3333
+ "to": "@agent-native/toolkit/app/sharing"
3334
3334
  },
3335
3335
  "ShareButton": {
3336
3336
  "to": "@agent-native/toolkit/app/sharing"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.216.0",
3
+ "version": "0.218.0",
4
4
  "description": "The agentic application framework for building autonomous agents with intuitive UIs",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -552,6 +552,7 @@
552
552
  "./client/onboarding/first-run-registry": "./dist/client/onboarding/first-run-registry.js",
553
553
  "./client/onboarding/first-run-status": "./dist/client/onboarding/first-run-status.js",
554
554
  "./client/onboarding/use-onboarding": "./dist/client/onboarding/use-onboarding.js",
555
+ "./client/onboarding/use-onboarding-checklist": "./dist/client/onboarding/use-onboarding-checklist.js",
555
556
  "./client/onboarding/use-preview-mode": "./dist/client/onboarding/use-preview-mode.js",
556
557
  "./client/org/hooks": "./dist/client/org/hooks.js",
557
558
  "./client/resources/mcp-connection-resume": "./dist/client/resources/mcp-connection-resume.js",
@@ -601,6 +602,7 @@
601
602
  "./notifications/types": "./dist/notifications/types.js",
602
603
  "./observability/types": "./dist/observability/types.js",
603
604
  "./onboarding/types": "./dist/onboarding/types.js",
605
+ "./onboarding/status": "./dist/onboarding/status.js",
604
606
  "./onboarding/workspace-services": "./dist/onboarding/workspace-services.js",
605
607
  "./onboarding/app-profile-data": "./dist/onboarding/app-profile-data.js",
606
608
  "./org/app-roles": "./dist/org/app-roles.js",
@@ -728,8 +730,8 @@
728
730
  "yaml": "^2.9.0",
729
731
  "yjs": "^13.6.33",
730
732
  "zod": "^4.3.6",
731
- "@agent-native/agentkit": "^0.216.0",
732
- "@agent-native/recap-cli": "^0.5.85"
733
+ "@agent-native/recap-cli": "^0.5.87",
734
+ "@agent-native/agentkit": "^0.218.0"
733
735
  },
734
736
  "devDependencies": {
735
737
  "@ai-sdk/anthropic": "^3.0.71",
@@ -9,6 +9,7 @@
9
9
  "app/components/layout/AgentInspector.test.tsx",
10
10
  "app/components/layout/AgentInspector.tsx",
11
11
  "app/components/layout/Header.tsx",
12
+ "app/components/layout/Sidebar.spec.tsx",
12
13
  "app/components/layout/Sidebar.tsx",
13
14
  "app/i18n",
14
15
  "app/i18n-data.ts",
@@ -1,3 +1,4 @@
1
+ import { appPath } from "@agent-native/core/client/api-path";
1
2
  import { useT } from "@agent-native/core/client/i18n";
2
3
  import {
3
4
  navigateWithAgentChatViewTransition,
@@ -5,14 +6,15 @@ import {
5
6
  useChatThreads,
6
7
  type ChatThreadSummary,
7
8
  } from "@agent-native/toolkit/app/chat/agentkit-chat/rail";
9
+ import {
10
+ ownDraftRows,
11
+ OwnershipChatHistoryRail,
12
+ useOwnershipChatHistory,
13
+ type OwnDraftRow,
14
+ } from "@agent-native/toolkit/app/chat/ownership-history";
8
15
  import { OrgSwitcher } from "@agent-native/toolkit/app/org";
9
16
  import { openCommandMenu } from "@agent-native/toolkit/app/shared";
10
17
  import { AgentNativeIcon } from "@agent-native/toolkit/app/shared/AgentNativeIcon";
11
- import {
12
- ChatHistoryList,
13
- type ChatHistoryItem,
14
- type ChatHistorySection,
15
- } from "@agent-native/toolkit/chat-history/ChatHistoryList";
16
18
  import {
17
19
  IconApps,
18
20
  IconClock,
@@ -25,7 +27,7 @@ import {
25
27
  IconPin,
26
28
  IconSearch,
27
29
  } from "@tabler/icons-react";
28
- import { useEffect, useMemo, useRef, useState } from "react";
30
+ import { useEffect, useRef, useState } from "react";
29
31
  import { Link, useLocation, useNavigate } from "react-router";
30
32
  import { toast } from "sonner";
31
33
 
@@ -41,7 +43,6 @@ import {
41
43
  } from "@/components/ui/tooltip";
42
44
  import { APP_TITLE } from "@/lib/app-config";
43
45
  import { chatThreadPath, HOME_PATH, threadIdFromPath } from "@/lib/chat-paths";
44
- import { visibleChatThreads } from "@/lib/sidebar-thread-state";
45
46
  import { cn } from "@/lib/utils";
46
47
 
47
48
  const CHAT_STORAGE_KEY = "chat";
@@ -53,11 +54,9 @@ interface SidebarProps {
53
54
  onCollapsedChange?: (collapsed: boolean) => void;
54
55
  }
55
56
 
56
- function threadTitle(thread: ChatThreadSummary, untitledLabel: string) {
57
- return thread.title || untitledLabel;
58
- }
59
-
60
- function threadUpdatedAt(thread: ChatThreadSummary) {
57
+ function threadUpdatedAt(
58
+ thread: Pick<ChatThreadSummary, "createdAt" | "updatedAt">,
59
+ ) {
61
60
  return Number.isFinite(thread.updatedAt)
62
61
  ? thread.updatedAt
63
62
  : Number.isFinite(thread.createdAt)
@@ -65,14 +64,6 @@ function threadUpdatedAt(thread: ChatThreadSummary) {
65
64
  : 0;
66
65
  }
67
66
 
68
- function compareRecentThreads(a: ChatThreadSummary, b: ChatThreadSummary) {
69
- return threadUpdatedAt(b) - threadUpdatedAt(a);
70
- }
71
-
72
- function comparePinnedThreads(a: ChatThreadSummary, b: ChatThreadSummary) {
73
- return (b.pinnedAt ?? 0) - (a.pinnedAt ?? 0);
74
- }
75
-
76
67
  function formatRelativeTime(timestamp: number) {
77
68
  const elapsed = timestamp - Date.now();
78
69
  const intervals = [
@@ -106,7 +97,10 @@ function ThreadHistoryTitle({
106
97
  title,
107
98
  running,
108
99
  }: {
109
- thread: ChatThreadSummary;
100
+ thread: OwnDraftRow &
101
+ Partial<
102
+ Pick<ChatThreadSummary, "preview" | "scope" | "source" | "pinnedAt">
103
+ >;
110
104
  title: string;
111
105
  running: boolean;
112
106
  }) {
@@ -287,7 +281,7 @@ function ChatThreadsSection({ collapsed }: { collapsed: boolean }) {
287
281
  pinThread,
288
282
  archiveThread,
289
283
  renameThread,
290
- refreshThreads,
284
+ isNewThread,
291
285
  } = useChatThreads(undefined, CHAT_STORAGE_KEY, undefined, {
292
286
  autoCreate: false,
293
287
  restoreActiveThread: false,
@@ -295,66 +289,46 @@ function ChatThreadsSection({ collapsed }: { collapsed: boolean }) {
295
289
  const { workingThreadIds, observedThreadStarts } =
296
290
  useAgentChatRunningThreads();
297
291
 
298
- const visibleThreads = useMemo(
299
- () => visibleChatThreads(threads, observedThreadStarts),
300
- [observedThreadStarts, threads],
301
- );
302
- const displayedActiveThreadId =
303
- threadIdFromPath(location.pathname) ??
304
- activeThreadId ??
305
- persistedActiveThreadId();
306
-
307
- const historySections = useMemo<ChatHistorySection[]>(() => {
308
- const untitledLabel = t("chat.untitledChat");
309
- const toItem = (thread: ChatThreadSummary): ChatHistoryItem => {
310
- const title = threadTitle(thread, untitledLabel);
292
+ const localMineRows = ownDraftRows({
293
+ threads,
294
+ isNewThread,
295
+ observedThreadStarts,
296
+ });
297
+ const history = useOwnershipChatHistory({
298
+ getThreadUrl: (threadId) => appPath(chatThreadPath(threadId)),
299
+ activeId:
300
+ threadIdFromPath(location.pathname) ??
301
+ activeThreadId ??
302
+ persistedActiveThreadId(),
303
+ localMineRows,
304
+ pinned: { label: t("chat.pinned") },
305
+ mutations: {
306
+ pin: pinThread,
307
+ rename: renameThread,
308
+ archive: archiveThread,
309
+ },
310
+ onMutationFailed: (kind) => {
311
+ if (kind === "archive") toast.error(t("chat.archiveFailed"));
312
+ if (kind === "rename") toast.error(t("chat.renameFailed"));
313
+ },
314
+ decorateItem: (item, row) => {
315
+ const title = row.title || row.preview || t("chat.untitledChat");
311
316
  return {
312
- id: thread.id,
317
+ ...item,
313
318
  title: (
314
319
  <ThreadHistoryTitle
315
- thread={thread}
320
+ thread={row}
316
321
  title={title}
317
- running={workingThreadIds.has(thread.id)}
322
+ running={workingThreadIds.has(row.id)}
318
323
  />
319
324
  ),
320
- titleText: title,
321
- pinned: Boolean(thread.pinnedAt),
322
325
  };
323
- };
324
- const pinned = visibleThreads
325
- .filter((thread) => Boolean(thread.pinnedAt))
326
- .sort(comparePinnedThreads)
327
- .map(toItem);
328
- const recent = visibleThreads
329
- .filter((thread) => !thread.pinnedAt)
330
- .sort(compareRecentThreads)
331
- .slice(0, 30)
332
- .map(toItem);
333
-
334
- return [
335
- { id: "pinned", label: t("chat.pinned"), items: pinned },
336
- { id: "recents", label: t("chat.recents"), items: recent },
337
- ];
338
- }, [t, visibleThreads, workingThreadIds]);
339
-
340
- useEffect(() => {
341
- const refresh = () => refreshThreads();
342
- const handleRunning = (event: Event) => {
343
- const detail = (event as CustomEvent).detail as
344
- | { isRunning?: unknown }
345
- | undefined;
346
- if (typeof detail?.isRunning === "boolean") refreshThreads();
347
- };
348
-
349
- window.addEventListener("agent-chat:threads-updated", refresh);
350
- window.addEventListener("agentNative.chatRunning", handleRunning);
351
- window.addEventListener("focus", refresh);
352
- return () => {
353
- window.removeEventListener("agent-chat:threads-updated", refresh);
354
- window.removeEventListener("agentNative.chatRunning", handleRunning);
355
- window.removeEventListener("focus", refresh);
356
- };
357
- }, [refreshThreads]);
326
+ },
327
+ });
328
+ const displayedActiveThreadId =
329
+ threadIdFromPath(location.pathname) ??
330
+ activeThreadId ??
331
+ persistedActiveThreadId();
358
332
 
359
333
  function openThread(threadId: string, options?: { isNew?: boolean }) {
360
334
  switchThread(threadId);
@@ -382,23 +356,6 @@ function ChatThreadsSection({ collapsed }: { collapsed: boolean }) {
382
356
  if (threadId) openThread(threadId, { isNew: true });
383
357
  }
384
358
 
385
- async function handleArchiveThread(threadId: string) {
386
- const wasActive =
387
- threadId === activeThreadId || threadId === persistedActiveThreadId();
388
- const archived = await archiveThread(threadId);
389
- if (!archived) {
390
- toast.error(t("chat.archiveFailed"));
391
- return;
392
- }
393
- if (wasActive) await handleNewChat();
394
- }
395
-
396
- function handleRenameThread(threadId: string, title: string) {
397
- void renameThread(threadId, title).then((renamed) => {
398
- if (!renamed) toast.error(t("chat.renameFailed"));
399
- });
400
- }
401
-
402
359
  const newChatButton = (
403
360
  <button
404
361
  type="button"
@@ -430,17 +387,20 @@ function ChatThreadsSection({ collapsed }: { collapsed: boolean }) {
430
387
  return (
431
388
  <div className="flex min-h-0 flex-1 flex-col">
432
389
  <div className="px-2 pb-3">{newChatButton}</div>
433
- <ChatHistoryList
434
- sections={historySections}
390
+ <OwnershipChatHistoryRail
391
+ {...history}
392
+ onNewChat={() => void handleNewChat()}
393
+ showNewChat={false}
435
394
  activeId={displayedActiveThreadId}
436
395
  onSelect={openThread}
437
- onTogglePin={(threadId) => {
438
- const thread = visibleThreads.find((item) => item.id === threadId);
439
- if (thread) void pinThread(threadId, !thread.pinnedAt);
396
+ onDelete={async (threadId) => {
397
+ const wasActive =
398
+ threadId === activeThreadId ||
399
+ threadId === persistedActiveThreadId();
400
+ const archived = await history.listProps.onDelete?.(threadId);
401
+ if (archived && wasActive) await handleNewChat();
440
402
  }}
441
- onRename={handleRenameThread}
442
403
  renameMaxLength={160}
443
- onDelete={(threadId) => void handleArchiveThread(threadId)}
444
404
  labels={{
445
405
  options: (item) =>
446
406
  t("chat.optionsFor", { title: item.titleText ?? "" }),
@@ -451,8 +411,9 @@ function ChatThreadsSection({ collapsed }: { collapsed: boolean }) {
451
411
  unpin: t("chat.unpinChat"),
452
412
  delete: t("chat.archiveChat"),
453
413
  }}
454
- variant="rail"
455
- emptyLabel={null}
414
+ railLabels={{
415
+ newChat: t("chat.newChat"),
416
+ }}
456
417
  className="chat-sidebar-history min-h-0 flex-1"
457
418
  listClassName="pb-4"
458
419
  />
@@ -1,29 +0,0 @@
1
- import type { ChatThreadSummary } from "@agent-native/toolkit/app/chat/agentkit-chat/index";
2
-
3
- export function visibleChatThreads(
4
- threads: ChatThreadSummary[],
5
- observedThreadStarts: ReadonlyMap<string, number>,
6
- ): ChatThreadSummary[] {
7
- const knownIds = new Set(threads.map((thread) => thread.id));
8
- const optimistic: ChatThreadSummary[] = [...observedThreadStarts].flatMap(
9
- ([id, startedAt]) =>
10
- knownIds.has(id)
11
- ? []
12
- : [
13
- {
14
- id,
15
- title: "",
16
- preview: "",
17
- messageCount: 0,
18
- createdAt: startedAt,
19
- updatedAt: startedAt,
20
- scope: null,
21
- },
22
- ],
23
- );
24
- return [...threads, ...optimistic].filter(
25
- (thread) =>
26
- !thread.archivedAt &&
27
- (thread.messageCount > 0 || observedThreadStarts.has(thread.id)),
28
- );
29
- }
@@ -1,29 +0,0 @@
1
- import type { ChatThreadSummary } from "@agent-native/toolkit/app/chat/agentkit-chat/index";
2
-
3
- export function visibleChatThreads(
4
- threads: ChatThreadSummary[],
5
- observedThreadStarts: ReadonlyMap<string, number>,
6
- ): ChatThreadSummary[] {
7
- const knownIds = new Set(threads.map((thread) => thread.id));
8
- const optimistic: ChatThreadSummary[] = [...observedThreadStarts].flatMap(
9
- ([id, startedAt]) =>
10
- knownIds.has(id)
11
- ? []
12
- : [
13
- {
14
- id,
15
- title: "",
16
- preview: "",
17
- messageCount: 0,
18
- createdAt: startedAt,
19
- updatedAt: startedAt,
20
- scope: null,
21
- },
22
- ],
23
- );
24
- return [...threads, ...optimistic].filter(
25
- (thread) =>
26
- !thread.archivedAt &&
27
- (thread.messageCount > 0 || observedThreadStarts.has(thread.id)),
28
- );
29
- }