@agent-native/core 0.107.0 → 0.107.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 (156) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +16 -0
  3. package/corpus/core/docs/content/integrations.mdx +27 -27
  4. package/corpus/core/package.json +1 -1
  5. package/corpus/core/src/a2a/artifact-response.ts +32 -10
  6. package/corpus/core/src/agent/production-agent.ts +19 -0
  7. package/corpus/core/src/agent/run-store.ts +98 -2
  8. package/corpus/core/src/client/AssistantChat.tsx +13 -0
  9. package/corpus/core/src/client/CommandMenu.tsx +5 -0
  10. package/corpus/core/src/client/active-run-state.ts +34 -0
  11. package/corpus/core/src/client/agent-chat-adapter.ts +4 -0
  12. package/corpus/core/src/client/resources/McpConnectionSuggestion.tsx +41 -11
  13. package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +27 -9
  14. package/corpus/core/src/client/resources/mcp-integration-catalog.ts +45 -0
  15. package/corpus/core/src/client/sharing/ShareButton.tsx +30 -9
  16. package/corpus/core/src/client/use-agent-chat-home-handoff.ts +2 -2
  17. package/corpus/core/src/integrations/a2a-continuation-processor.ts +107 -3
  18. package/corpus/core/src/integrations/pending-tasks-store.ts +98 -1
  19. package/corpus/core/src/integrations/plugin.ts +129 -4
  20. package/corpus/core/src/integrations/webhook-handler.ts +339 -36
  21. package/corpus/core/src/localization/default-messages.ts +4 -1
  22. package/corpus/core/src/secrets/register-framework-secrets.ts +28 -0
  23. package/corpus/core/src/server/agent-chat-plugin.ts +122 -9
  24. package/corpus/core/src/vite/client.ts +24 -0
  25. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +29 -21
  26. package/corpus/templates/analytics/AGENTS.md +8 -0
  27. package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +1 -1
  28. package/corpus/templates/analytics/actions/update-dashboard.ts +6 -2
  29. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +35 -7
  30. package/corpus/templates/analytics/app/components/layout/Layout.tsx +1 -3
  31. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +11 -1
  32. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/extension-slot.ts +11 -0
  33. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +27 -0
  34. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +6 -0
  35. package/corpus/templates/analytics/changelog/2026-07-17-dashboard-extension-boxes.md +6 -0
  36. package/corpus/templates/analytics/server/lib/agent-readable-resource-context.ts +8 -0
  37. package/corpus/templates/analytics/server/plugins/agent-chat.ts +1 -0
  38. package/corpus/templates/assets/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
  39. package/corpus/templates/brain/app/hooks/use-navigation-state.ts +6 -1
  40. package/corpus/templates/brain/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
  41. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +1 -1
  42. package/corpus/templates/calendar/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
  43. package/corpus/templates/chat/app/hooks/use-navigation-state.ts +4 -1
  44. package/corpus/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
  45. package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
  46. package/corpus/templates/clips/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
  47. package/corpus/templates/content/actions/_content-space-access.ts +45 -2
  48. package/corpus/templates/content/actions/create-content-database.ts +41 -7
  49. package/corpus/templates/content/app/components/layout/Layout.tsx +1 -1
  50. package/corpus/templates/content/app/root.tsx +1 -10
  51. package/corpus/templates/content/changelog/2026-07-17-creating-a-database-now-completes-reliably-in-hosted-workspa.md +6 -0
  52. package/corpus/templates/content/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
  53. package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +4 -1
  54. package/corpus/templates/dispatch/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
  55. package/corpus/templates/forms/app/components/layout/Layout.tsx +2 -4
  56. package/corpus/templates/forms/app/components/ui/popover.tsx +25 -1
  57. package/corpus/templates/forms/app/hooks/use-navigation-state.ts +6 -1
  58. package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +13 -10
  59. package/corpus/templates/forms/app/root.tsx +6 -1
  60. package/corpus/templates/forms/changelog/2026-07-17-form-builder-popovers-stay-within-the-viewport.md +6 -0
  61. package/corpus/templates/forms/changelog/2026-07-17-form-editor-loading-matches-current-sidebar-free-layout.md +6 -0
  62. package/corpus/templates/forms/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
  63. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +1 -1
  64. package/corpus/templates/mail/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
  65. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +14 -3
  66. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +8 -1
  67. package/corpus/templates/plan/app/root.tsx +0 -2
  68. package/corpus/templates/plan/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
  69. package/corpus/templates/slides/app/components/layout/Layout.tsx +1 -1
  70. package/corpus/templates/slides/app/context/DeckContext.tsx +23 -23
  71. package/corpus/templates/slides/changelog/2026-07-17-duplicated-slides-now-stay-in-the-deck.md +6 -0
  72. package/corpus/templates/slides/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
  73. package/corpus/templates/tasks/app/components/layout/Layout.tsx +2 -13
  74. package/corpus/templates/tasks/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it.md +6 -0
  75. package/dist/a2a/artifact-response.d.ts +5 -1
  76. package/dist/a2a/artifact-response.d.ts.map +1 -1
  77. package/dist/a2a/artifact-response.js +18 -12
  78. package/dist/a2a/artifact-response.js.map +1 -1
  79. package/dist/agent/production-agent.d.ts.map +1 -1
  80. package/dist/agent/production-agent.js +13 -1
  81. package/dist/agent/production-agent.js.map +1 -1
  82. package/dist/agent/run-store.d.ts +17 -0
  83. package/dist/agent/run-store.d.ts.map +1 -1
  84. package/dist/agent/run-store.js +84 -2
  85. package/dist/agent/run-store.js.map +1 -1
  86. package/dist/client/AssistantChat.d.ts.map +1 -1
  87. package/dist/client/AssistantChat.js +10 -1
  88. package/dist/client/AssistantChat.js.map +1 -1
  89. package/dist/client/CommandMenu.d.ts.map +1 -1
  90. package/dist/client/CommandMenu.js +4 -0
  91. package/dist/client/CommandMenu.js.map +1 -1
  92. package/dist/client/active-run-state.d.ts +7 -0
  93. package/dist/client/active-run-state.d.ts.map +1 -1
  94. package/dist/client/active-run-state.js +28 -0
  95. package/dist/client/active-run-state.js.map +1 -1
  96. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  97. package/dist/client/agent-chat-adapter.js +4 -1
  98. package/dist/client/agent-chat-adapter.js.map +1 -1
  99. package/dist/client/resources/McpConnectionSuggestion.d.ts.map +1 -1
  100. package/dist/client/resources/McpConnectionSuggestion.js +27 -8
  101. package/dist/client/resources/McpConnectionSuggestion.js.map +1 -1
  102. package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
  103. package/dist/client/resources/McpIntegrationDialog.js +4 -2
  104. package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
  105. package/dist/client/resources/mcp-integration-catalog.d.ts +6 -0
  106. package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
  107. package/dist/client/resources/mcp-integration-catalog.js +33 -0
  108. package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
  109. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  110. package/dist/client/sharing/ShareButton.js +15 -12
  111. package/dist/client/sharing/ShareButton.js.map +1 -1
  112. package/dist/client/use-agent-chat-home-handoff.d.ts +1 -1
  113. package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
  114. package/dist/client/use-agent-chat-home-handoff.js +1 -1
  115. package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
  116. package/dist/collab/awareness.d.ts +2 -2
  117. package/dist/collab/awareness.d.ts.map +1 -1
  118. package/dist/collab/routes.d.ts +1 -1
  119. package/dist/integrations/a2a-continuation-processor.js +62 -5
  120. package/dist/integrations/a2a-continuation-processor.js.map +1 -1
  121. package/dist/integrations/pending-tasks-store.d.ts +3 -0
  122. package/dist/integrations/pending-tasks-store.d.ts.map +1 -1
  123. package/dist/integrations/pending-tasks-store.js +75 -1
  124. package/dist/integrations/pending-tasks-store.js.map +1 -1
  125. package/dist/integrations/plugin.d.ts.map +1 -1
  126. package/dist/integrations/plugin.js +81 -5
  127. package/dist/integrations/plugin.js.map +1 -1
  128. package/dist/integrations/webhook-handler.d.ts +23 -2
  129. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  130. package/dist/integrations/webhook-handler.js +257 -35
  131. package/dist/integrations/webhook-handler.js.map +1 -1
  132. package/dist/localization/default-messages.d.ts +2 -0
  133. package/dist/localization/default-messages.d.ts.map +1 -1
  134. package/dist/localization/default-messages.js +3 -1
  135. package/dist/localization/default-messages.js.map +1 -1
  136. package/dist/notifications/routes.d.ts +2 -2
  137. package/dist/observability/routes.d.ts +5 -5
  138. package/dist/resources/handlers.d.ts +1 -1
  139. package/dist/secrets/register-framework-secrets.d.ts.map +1 -1
  140. package/dist/secrets/register-framework-secrets.js +25 -0
  141. package/dist/secrets/register-framework-secrets.js.map +1 -1
  142. package/dist/secrets/routes.d.ts +9 -9
  143. package/dist/server/agent-chat-plugin.d.ts +6 -0
  144. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  145. package/dist/server/agent-chat-plugin.js +94 -7
  146. package/dist/server/agent-chat-plugin.js.map +1 -1
  147. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  148. package/dist/templates/chat/app/hooks/use-navigation-state.ts +4 -1
  149. package/dist/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
  150. package/dist/vite/client.d.ts.map +1 -1
  151. package/dist/vite/client.js +20 -0
  152. package/dist/vite/client.js.map +1 -1
  153. package/docs/content/integrations.mdx +27 -27
  154. package/package.json +1 -1
  155. package/src/templates/chat/app/hooks/use-navigation-state.ts +4 -1
  156. package/src/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
@@ -704,6 +704,7 @@ export default createAgentChatPlugin({
704
704
  analyticsSourceGuidanceOpening() +
705
705
  "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. " +
706
706
  "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. " +
707
+ 'DASHBOARD EXTENSION BOX RULE — Analytics dashboards support extension boxes. Insert `chartType: "extension"` panels with `config.extensionSlotId` set to `analytics.dashboard.<dashboard-id>.panel.<panel-id>`, then call `add-extension-slot-target` and `install-extension` with that same id. The install is per-user; the extension box stays on the shared dashboard and receives dashboard/panel/current-filter context. Use `get-sql-dashboard` panel summaries to inspect an existing box; `config.extensionId` is only the legacy direct-embed format. ' +
707
708
  "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 use `update-extension` `patches`/`edits` that change only the data-loading seam. Preserve the existing layout, CSS, copy, and interactions; never reconstruct the full HTML body for a data-only fix. Full-body replacement is blocked unless `allowFullReplacement=true`, which is reserved for an explicit visual rewrite or a complete replacement body supplied by the user. If a focused edit fails, change the target instead of retrying identical arguments. " +
708
709
  'FIRST-PARTY DASHBOARD TIME RULE — AI-generated `source: "first-party"` panels are dashboard-time-bound by default: set `config.timeScope` to `dashboard` and include a matching dashboard time predicate. `{{timeRange}}` requires a matching `filters` entry with `id: "timeRange"` and `type: "select"`; `{{<id>Start}}`/`{{<id>End}}` require a matching `type: "date-range"` filter with that id. Allowed `timeScope` values are `dashboard`, `fixed-window`, `cohort-history`, and `all-time`; use `all-time` only when the user requests full available history and put all-time, lifetime, or historical in the title or description. Server validation rejects unbound first-party SQL. ' +
709
710
  "DASHBOARD READ RULE — `get-sql-dashboard` is compact by default: use its `panels` summaries plus `layout.panelOrder`, `layout.firstPanelIds`, and `layout.groups[].rows[].rowNumber/panelIds` for orientation and verification. Pass `includeConfig: true` only when you truly need full panel SQL/config. " +
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-17
4
+ ---
5
+
6
+ The agent chat sidebar stays closed until you open it or start a chat handoff.
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  appBasePath,
3
3
  appPath,
4
+ isAgentChatHomeHandoffActive,
4
5
  markAgentChatHomeHandoff,
5
6
  useAgentRouteState,
6
7
  } from "@agent-native/core/client";
@@ -106,7 +107,11 @@ export function useNavigationState() {
106
107
  return `${path}${params.size ? `?${params.toString()}` : ""}`;
107
108
  },
108
109
  onNavigate: (_command, path) => {
109
- if (location.pathname === "/" && pathnameFromPath(path) !== "/") {
110
+ if (
111
+ location.pathname === "/" &&
112
+ pathnameFromPath(path) !== "/" &&
113
+ isAgentChatHomeHandoffActive("brain")
114
+ ) {
110
115
  markAgentChatHomeHandoff("brain");
111
116
  }
112
117
  },
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-17
4
+ ---
5
+
6
+ The agent chat sidebar stays closed until you open it or start a chat handoff.
@@ -390,7 +390,7 @@ export function AppLayout({ children }: AppLayoutProps) {
390
390
  />
391
391
  <AgentSidebar
392
392
  position="right"
393
- defaultOpen
393
+ defaultOpen={false}
394
394
  emptyStateText={t("agentSidebar.emptyState")}
395
395
  agentPageHref="/agent"
396
396
  suggestions={[
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-17
4
+ ---
5
+
6
+ The agent chat sidebar stays closed until you open it or start a chat handoff.
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  appBasePath,
3
+ isAgentChatHomeHandoffActive,
3
4
  appPath,
4
5
  markAgentChatHomeHandoff,
5
6
  useAgentRouteState,
@@ -34,7 +35,9 @@ export function useNavigationState() {
34
35
  isChatPath(location.pathname) &&
35
36
  !isChatPath(pathnameFromPath(path))
36
37
  ) {
37
- markAgentChatHomeHandoff("chat");
38
+ if (isAgentChatHomeHandoffActive("chat")) {
39
+ markAgentChatHomeHandoff("chat");
40
+ }
38
41
  }
39
42
  },
40
43
  });
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-17
4
+ ---
5
+
6
+ The agent chat sidebar stays closed until you open it or start a chat handoff.
@@ -559,7 +559,7 @@ export function LibraryLayout({ children }: LibraryLayoutProps) {
559
559
 
560
560
  <AgentSidebar
561
561
  position="right"
562
- defaultOpen={!isMobile}
562
+ defaultOpen={false}
563
563
  emptyStateText={t("navigation.agentEmptyState")}
564
564
  suggestions={[
565
565
  t("navigation.agentSuggestionSummary"),
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-17
4
+ ---
5
+
6
+ The agent chat sidebar stays closed until you open it or start a chat handoff.
@@ -1,9 +1,25 @@
1
1
  import { getDbExec } from "@agent-native/core/db";
2
+ import { table, text } from "@agent-native/core/db/schema";
2
3
  import { getRequestUserEmail } from "@agent-native/core/server/request-context";
3
- import { eq } from "drizzle-orm";
4
+ import { and, eq, sql } from "drizzle-orm";
4
5
 
5
6
  import { getDb, schema } from "../server/db/index.js";
6
7
 
8
+ // Keep these lightweight table references local. Importing the public org
9
+ // barrel also initializes auth's long-lived cleanup timer, which breaks test
10
+ // suites that intentionally drain fake timers.
11
+ const organizations = table("organizations", {
12
+ id: text("id").primaryKey(),
13
+ name: text("name").notNull(),
14
+ createdBy: text("created_by").notNull(),
15
+ });
16
+
17
+ const orgMembers = table("org_members", {
18
+ orgId: text("org_id").notNull(),
19
+ email: text("email").notNull(),
20
+ role: text("role").notNull(),
21
+ });
22
+
7
23
  export type ContentSpaceRole = "viewer" | "editor" | "owner";
8
24
 
9
25
  export type ContentSpaceAccess = {
@@ -21,7 +37,32 @@ export function normalizeContentSpaceEmail(email: string): string {
21
37
  export async function getContentOrganizationMembership(
22
38
  orgId: string,
23
39
  userEmail: string,
40
+ options: { db?: any } = {},
24
41
  ): Promise<{ role: string; name: string; createdBy: string } | null> {
42
+ if (options.db) {
43
+ const [row] = await options.db
44
+ .select({
45
+ role: orgMembers.role,
46
+ name: organizations.name,
47
+ createdBy: organizations.createdBy,
48
+ })
49
+ .from(orgMembers)
50
+ .innerJoin(organizations, eq(organizations.id, orgMembers.orgId))
51
+ .where(
52
+ and(
53
+ eq(orgMembers.orgId, orgId),
54
+ sql`LOWER(${orgMembers.email}) = ${normalizeContentSpaceEmail(userEmail)}`,
55
+ ),
56
+ )
57
+ .limit(1);
58
+ return row
59
+ ? {
60
+ role: String(row.role ?? "member").toLowerCase(),
61
+ name: row.name,
62
+ createdBy: row.createdBy,
63
+ }
64
+ : null;
65
+ }
25
66
  const result = await getDbExec().execute({
26
67
  sql: `SELECT m.role AS role, o.name AS name, o.created_by AS "createdBy"
27
68
  FROM org_members m
@@ -90,11 +131,12 @@ export async function listContentOrganizationMemberships(userEmail: string) {
90
131
  export async function resolveContentSpaceAccess(
91
132
  spaceId: string,
92
133
  requiredRole: "viewer" | "editor" = "viewer",
134
+ options: { db?: any } = {},
93
135
  ): Promise<ContentSpaceAccess> {
94
136
  const userEmail = getRequestUserEmail();
95
137
  if (!userEmail) throw new Error("no authenticated user");
96
138
  const normalizedUserEmail = normalizeContentSpaceEmail(userEmail);
97
- const [space] = await getDb()
139
+ const [space] = await (options.db ?? getDb())
98
140
  .select()
99
141
  .from(schema.contentSpaces)
100
142
  .where(eq(schema.contentSpaces.id, spaceId));
@@ -115,6 +157,7 @@ export async function resolveContentSpaceAccess(
115
157
  const membership = await getContentOrganizationMembership(
116
158
  space.orgId,
117
159
  normalizedUserEmail,
160
+ options,
118
161
  );
119
162
  if (!membership)
120
163
  throw new Error(`Not authorized for Content space "${spaceId}"`);
@@ -5,7 +5,13 @@ import {
5
5
  getRequestOrgId,
6
6
  getRequestUserEmail,
7
7
  } from "@agent-native/core/server/request-context";
8
- import { assertAccess, type ShareRole } from "@agent-native/core/sharing";
8
+ import {
9
+ accessFilter,
10
+ assertAccess,
11
+ currentAccess,
12
+ ForbiddenError,
13
+ type ShareRole,
14
+ } from "@agent-native/core/sharing";
9
15
  import { and, eq, sql } from "drizzle-orm";
10
16
  import { z } from "zod";
11
17
 
@@ -158,9 +164,34 @@ export async function resolveContentDatabaseSpace(
158
164
  return spaceId;
159
165
  }
160
166
 
167
+ async function assertDocumentEditorAccess(db: any, documentId: string) {
168
+ const [document] = await db
169
+ .select()
170
+ .from(schema.documents)
171
+ .where(
172
+ and(
173
+ eq(schema.documents.id, documentId),
174
+ accessFilter(
175
+ schema.documents,
176
+ schema.documentShares,
177
+ currentAccess(),
178
+ "editor",
179
+ ),
180
+ ),
181
+ );
182
+ if (!document) {
183
+ throw new ForbiddenError(`No editor access to document ${documentId}`);
184
+ }
185
+ return document;
186
+ }
187
+
161
188
  export async function createContentDatabaseRecord(
162
189
  args: CreateDatabaseRequest,
163
- options: { db?: any; spaceId?: string } = {},
190
+ options: {
191
+ db?: any;
192
+ spaceId?: string;
193
+ resolveSpaceAccess?: typeof resolveContentSpaceAccess;
194
+ } = {},
164
195
  ): Promise<string> {
165
196
  const db = options.db ?? getDb();
166
197
  const now = new Date().toISOString();
@@ -178,8 +209,7 @@ export async function createContentDatabaseRecord(
178
209
  }> = [];
179
210
 
180
211
  if (documentId) {
181
- const access = await assertAccess("document", documentId, "editor");
182
- const document = access.resource;
212
+ const document = await assertDocumentEditorAccess(db, documentId);
183
213
  ownerEmail = document.ownerEmail as string;
184
214
  orgId = (document.orgId as string | null) ?? null;
185
215
  spaceId = (document.spaceId as string | null) ?? spaceId;
@@ -230,8 +260,7 @@ export async function createContentDatabaseRecord(
230
260
  let hideFromSearch = 0;
231
261
 
232
262
  if (parentId) {
233
- const parentAccess = await assertAccess("document", parentId, "editor");
234
- const parent = parentAccess.resource;
263
+ const parent = await assertDocumentEditorAccess(db, parentId);
235
264
  ownerEmail = parent.ownerEmail as string;
236
265
  orgId = (parent.orgId as string | null) ?? null;
237
266
  spaceId = (parent.spaceId as string | null) ?? spaceId;
@@ -257,7 +286,12 @@ export async function createContentDatabaseRecord(
257
286
  "A top-level database requires a resolved Content space",
258
287
  );
259
288
  }
260
- const spaceAccess = await resolveContentSpaceAccess(spaceId, "editor");
289
+ const spaceAccess = await (
290
+ options.resolveSpaceAccess ?? resolveContentSpaceAccess
291
+ )(spaceId, "editor", { db });
292
+ if (spaceAccess.space.id !== spaceId) {
293
+ throw new Error("Resolved Content space does not match the request");
294
+ }
261
295
  ownerEmail = getRequestUserEmail() ?? ownerEmail;
262
296
  orgId = spaceAccess.space.orgId;
263
297
  visibility = orgId ? "org" : "private";
@@ -189,7 +189,7 @@ export function Layout({ children }: LayoutProps) {
189
189
  )}
190
190
  <AgentSidebar
191
191
  position="right"
192
- defaultOpen={!isMobile && !isNarrowDesktop}
192
+ defaultOpen={false}
193
193
  agentPageHref="/agent"
194
194
  emptyStateText={t("chat.emptyState")}
195
195
  suggestions={[
@@ -479,15 +479,6 @@ function PublicAgentShell({ children }: { children: React.ReactNode }) {
479
479
 
480
480
  useEffect(() => setMounted(true), []);
481
481
 
482
- useEffect(() => {
483
- if (!mounted) return;
484
- if (!window.matchMedia("(min-width: 768px)").matches) return;
485
- const id = window.setTimeout(() => {
486
- window.dispatchEvent(new Event("agent-panel:open"));
487
- }, 0);
488
- return () => window.clearTimeout(id);
489
- }, [mounted]);
490
-
491
482
  const content = <>{children}</>;
492
483
 
493
484
  if (!mounted) {
@@ -503,7 +494,7 @@ function PublicAgentShell({ children }: { children: React.ReactNode }) {
503
494
  return (
504
495
  <AgentSidebar
505
496
  position="right"
506
- defaultOpen
497
+ defaultOpen={false}
507
498
  defaultSidebarWidth={420}
508
499
  emptyStateText={t("chat.publicEmptyState")}
509
500
  suggestions={[
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-17
4
+ ---
5
+
6
+ Creating a database now completes reliably in hosted workspaces
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-17
4
+ ---
5
+
6
+ The agent chat sidebar stays closed until you open it or start a chat handoff.
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  appBasePath,
3
3
  appPath,
4
+ isAgentChatHomeHandoffActive,
4
5
  markAgentChatHomeHandoff,
5
6
  useAgentRouteState,
6
7
  } from "@agent-native/core/client";
@@ -54,7 +55,9 @@ export function useNavigationState(extensions?: DispatchExtensionConfig) {
54
55
  routerPath(location.pathname) === "/chat" &&
55
56
  pathnameFromPath(path) !== "/chat"
56
57
  ) {
57
- markAgentChatHomeHandoff("dispatch");
58
+ if (isAgentChatHomeHandoffActive("dispatch")) {
59
+ markAgentChatHomeHandoff("dispatch");
60
+ }
58
61
  }
59
62
  },
60
63
  });
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-17
4
+ ---
5
+
6
+ The agent chat sidebar stays closed until you open it or start a chat handoff.
@@ -51,8 +51,6 @@ export function Layout({ children }: LayoutProps) {
51
51
  if (!formId) return null;
52
52
  return { type: "form" as const, id: formId };
53
53
  }, [location.pathname]);
54
- const sidebarScope = chatHomeHandoffActive ? null : formScope;
55
-
56
54
  if (BARE_ROUTES.has(location.pathname)) {
57
55
  return <>{children}</>;
58
56
  }
@@ -89,7 +87,7 @@ export function Layout({ children }: LayoutProps) {
89
87
  <AgentSidebar
90
88
  position="right"
91
89
  agentPageHref="/agent"
92
- defaultOpen
90
+ defaultOpen={false}
93
91
  chatViewTransition
94
92
  storageKey="forms"
95
93
  browserTabId={TAB_ID}
@@ -101,7 +99,7 @@ export function Layout({ children }: LayoutProps) {
101
99
  t("agent.suggestionSubmissions"),
102
100
  t("agent.suggestionExport"),
103
101
  ]}
104
- scope={sidebarScope}
102
+ scope={formScope}
105
103
  >
106
104
  <div className="flex h-full flex-1 flex-col overflow-hidden">
107
105
  {showHeader ? <Header /> : null}
@@ -1 +1,25 @@
1
- export * from "@agent-native/toolkit/ui/popover";
1
+ import {
2
+ Popover,
3
+ PopoverAnchor,
4
+ PopoverContent as ToolkitPopoverContent,
5
+ PopoverTrigger,
6
+ } from "@agent-native/toolkit/ui/popover";
7
+ import * as React from "react";
8
+
9
+ import { cn } from "@/lib/utils";
10
+
11
+ const PopoverContent = React.forwardRef<
12
+ React.ElementRef<typeof ToolkitPopoverContent>,
13
+ React.ComponentPropsWithoutRef<typeof ToolkitPopoverContent>
14
+ >(({ className, collisionPadding = 16, ...props }, ref) => (
15
+ <ToolkitPopoverContent
16
+ ref={ref}
17
+ collisionPadding={collisionPadding}
18
+ className={cn("max-w-[calc(100vw-2rem)]", className)}
19
+ {...props}
20
+ />
21
+ ));
22
+
23
+ PopoverContent.displayName = ToolkitPopoverContent.displayName;
24
+
25
+ export { Popover, PopoverAnchor, PopoverContent, PopoverTrigger };
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  appBasePath,
3
+ isAgentChatHomeHandoffActive,
3
4
  markAgentChatHomeHandoff,
4
5
  useAgentRouteState,
5
6
  } from "@agent-native/core/client";
@@ -131,7 +132,11 @@ export function useNavigationState() {
131
132
  navigateOptions: { flushSync: true, replace: true },
132
133
  onNavigate: (_command, path) => {
133
134
  void prewarmFormsRoutePath(path);
134
- if (location.pathname === "/ask" && path !== "/ask") {
135
+ if (
136
+ location.pathname === "/ask" &&
137
+ path !== "/ask" &&
138
+ isAgentChatHomeHandoffActive("forms")
139
+ ) {
135
140
  markAgentChatHomeHandoff("forms");
136
141
  }
137
142
  },
@@ -359,10 +359,19 @@ export function FormBuilderPage() {
359
359
  <Skeleton className="h-8 w-20 rounded-md" />
360
360
  </div>
361
361
  </div>
362
- {/* Body: builder + properties */}
363
- <div className="flex flex-1 overflow-hidden">
364
- <div className="flex-1 overflow-auto p-4 sm:p-6">
365
- <div className="max-w-2xl mx-auto space-y-4">
362
+ {/* Tabs */}
363
+ <div className="border-b border-border px-2 sm:px-4 py-2 shrink-0 overflow-hidden">
364
+ <div className="flex w-max items-center gap-1 rounded-lg bg-muted/40 p-1">
365
+ <Skeleton className="h-8 w-12 rounded-md" />
366
+ <Skeleton className="h-8 w-16 rounded-md" />
367
+ <Skeleton className="h-8 w-16 rounded-md" />
368
+ <Skeleton className="h-8 w-24 rounded-md" />
369
+ </div>
370
+ </div>
371
+ {/* Body: builder */}
372
+ <div className="flex flex-1 overflow-hidden relative">
373
+ <div className="flex-1 overflow-auto bg-muted/30">
374
+ <div className="max-w-2xl mx-auto py-4 sm:py-8 px-3 sm:px-4 space-y-4">
366
375
  <Skeleton className="h-8 w-2/3" />
367
376
  <Skeleton className="h-4 w-1/2" />
368
377
  <div className="space-y-3 pt-4">
@@ -378,12 +387,6 @@ export function FormBuilderPage() {
378
387
  </div>
379
388
  </div>
380
389
  </div>
381
- <div className="hidden lg:block w-72 border-s border-border p-4 space-y-4">
382
- <Skeleton className="h-4 w-32" />
383
- <Skeleton className="h-9 w-full" />
384
- <Skeleton className="h-9 w-full" />
385
- <Skeleton className="h-9 w-full" />
386
- </div>
387
390
  </div>
388
391
  </div>
389
392
  );
@@ -8,6 +8,7 @@ import {
8
8
  useCommandMenuShortcut,
9
9
  getThemeInitScript,
10
10
  configureTracking,
11
+ isAgentChatHomeHandoffActive,
11
12
  markAgentChatHomeHandoff,
12
13
  navigateWithAgentChatViewTransition,
13
14
  setClientAppState,
@@ -199,7 +200,11 @@ function OpenLinkInterceptor() {
199
200
  if (!path) return;
200
201
 
201
202
  event.preventDefault();
202
- if (location.pathname === "/ask" && path !== "/ask") {
203
+ if (
204
+ location.pathname === "/ask" &&
205
+ path !== "/ask" &&
206
+ isAgentChatHomeHandoffActive("forms")
207
+ ) {
203
208
  markAgentChatHomeHandoff("forms");
204
209
  }
205
210
  navigateWithAgentChatViewTransition(navigate, path);
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-17
4
+ ---
5
+
6
+ Form builder popovers stay within the viewport
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-17
4
+ ---
5
+
6
+ Form editor loading now matches the current sidebar-free layout.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-17
4
+ ---
5
+
6
+ The agent chat sidebar stays closed until you open it or start a chat handoff.
@@ -196,7 +196,7 @@ export function AppLayout({ children }: AppLayoutProps) {
196
196
  return (
197
197
  <AgentSidebar
198
198
  position="right"
199
- defaultOpen={!isMobile}
199
+ defaultOpen={false}
200
200
  agentPageHref="/agent"
201
201
  emptyStateText={t("agent.emptyState")}
202
202
  suggestions={[
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-17
4
+ ---
5
+
6
+ The agent chat sidebar stays closed until you open it or start a chat handoff.
@@ -3,6 +3,7 @@ import {
3
3
  DevDatabaseLink,
4
4
  FeedbackButton,
5
5
  appPath,
6
+ isAgentChatHomeHandoffActive,
6
7
  markAgentChatHomeHandoff,
7
8
  navigateWithAgentChatViewTransition,
8
9
  PromptComposer,
@@ -442,13 +443,17 @@ function PlansSidebarSection({ collapsed }: { collapsed: boolean }) {
442
443
  signInForPlanCreate();
443
444
  return;
444
445
  }
445
- markAgentChatHomeHandoff("plans");
446
+ if (location.pathname === "/" && isAgentChatHomeHandoffActive("plans")) {
447
+ markAgentChatHomeHandoff("plans");
448
+ }
446
449
  navigateWithAgentChatViewTransition(navigate, "/plans?create=1");
447
450
  };
448
451
 
449
452
  const openPlanPath = (event: MouseEvent<HTMLAnchorElement>, path: string) => {
450
453
  event.preventDefault();
451
- markAgentChatHomeHandoff("plans");
454
+ if (location.pathname === "/" && isAgentChatHomeHandoffActive("plans")) {
455
+ markAgentChatHomeHandoff("plans");
456
+ }
452
457
  navigateWithAgentChatViewTransition(navigate, path);
453
458
  };
454
459
 
@@ -738,7 +743,13 @@ export function Sidebar({
738
743
  <Link
739
744
  to={item.href}
740
745
  onClick={() => {
741
- if (item.href !== "/") markAgentChatHomeHandoff("plans");
746
+ if (
747
+ item.href !== "/" &&
748
+ location.pathname === "/" &&
749
+ isAgentChatHomeHandoffActive("plans")
750
+ ) {
751
+ markAgentChatHomeHandoff("plans");
752
+ }
742
753
  }}
743
754
  className={cn(
744
755
  "flex items-center gap-3 rounded-lg px-3 py-2 text-sm transition-colors",
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  agentNativePath,
3
3
  appBasePath,
4
+ isAgentChatHomeHandoffActive,
4
5
  markAgentChatHomeHandoff,
5
6
  } from "@agent-native/core/client";
6
7
  import { useQuery, useQueryClient } from "@tanstack/react-query";
@@ -108,7 +109,13 @@ export function useNavigationState() {
108
109
  deleteCommand();
109
110
  const path = planNavigateCommandPath(cmd);
110
111
  void prewarmPlanRoutePath(path);
111
- if (path !== "/") markAgentChatHomeHandoff("plans");
112
+ if (
113
+ location.pathname === "/" &&
114
+ path !== "/" &&
115
+ isAgentChatHomeHandoffActive("plans")
116
+ ) {
117
+ markAgentChatHomeHandoff("plans");
118
+ }
112
119
  const commitNavigation = () =>
113
120
  navigate(path, { replace: true, flushSync: true });
114
121
  if (
@@ -6,7 +6,6 @@ import {
6
6
  createAgentNativeQueryClient,
7
7
  getLocaleInitScript,
8
8
  getThemeInitScript,
9
- markAgentChatHomeHandoff,
10
9
  navigateWithAgentChatViewTransition,
11
10
  useCommandMenuShortcut,
12
11
  useT,
@@ -153,7 +152,6 @@ function AppContent() {
153
152
  useCommandMenuShortcut(useCallback(() => setCmdkOpen(true), []));
154
153
  const go = useCallback(
155
154
  (path: string) => {
156
- if (path !== "/") markAgentChatHomeHandoff("plans");
157
155
  navigateWithAgentChatViewTransition(navigate, path);
158
156
  setCmdkOpen(false);
159
157
  },
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-17
4
+ ---
5
+
6
+ The agent chat sidebar stays closed until you open it or start a chat handoff.
@@ -69,7 +69,7 @@ export function Layout({ children }: LayoutProps) {
69
69
  <HeaderActionsProvider>
70
70
  <AgentSidebar
71
71
  position="right"
72
- defaultOpen
72
+ defaultOpen={false}
73
73
  emptyStateText={t("agent.emptyState")}
74
74
  suggestions={[
75
75
  t("agent.suggestionPitch"),