@agent-native/core 0.81.2 → 0.82.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 (81) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +25 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/client/frame.ts +55 -0
  5. package/corpus/core/src/file-upload/builder.ts +199 -37
  6. package/corpus/core/src/file-upload/index.ts +2 -0
  7. package/corpus/core/src/file-upload/types.ts +38 -0
  8. package/corpus/core/src/mcp/build-server.ts +30 -4
  9. package/corpus/core/src/mcp/embed-app.ts +33 -18
  10. package/corpus/templates/clips/actions/create-recording.ts +44 -0
  11. package/corpus/templates/clips/actions/finalize-recording.ts +198 -88
  12. package/corpus/templates/clips/actions/lib/create-recording-schema.ts +12 -0
  13. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +83 -29
  14. package/corpus/templates/clips/app/routes/record.tsx +12 -1
  15. package/corpus/templates/clips/server/lib/resumable-session.ts +39 -0
  16. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +2 -0
  17. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +186 -29
  18. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +4 -0
  19. package/corpus/templates/clips/shared/recording-core.ts +5 -0
  20. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +63 -16
  21. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +28 -33
  22. package/corpus/templates/content/app/components/sidebar/document-sidebar-sections.ts +47 -0
  23. package/corpus/templates/content/app/hooks/use-documents.ts +37 -0
  24. package/corpus/templates/content/changelog/2026-06-30-sidebar-favorites-now-keep-long-titles-tidy-and-stay-in-sync.md +6 -0
  25. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +8 -1
  26. package/corpus/templates/design/app/components/design/LayersPanel.tsx +11 -2
  27. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +89 -16
  28. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +21 -14
  29. package/corpus/templates/design/app/pages/DesignEditor.tsx +557 -211
  30. package/corpus/templates/design/changelog/2026-06-30-design-studio-canvas-layers-can-be-copied-pasted-and-reparen.md +6 -0
  31. package/corpus/templates/design/e2e/global-setup.ts +3 -1
  32. package/corpus/templates/design/e2e/helpers.ts +4 -1
  33. package/corpus/templates/design/playwright.config.ts +4 -1
  34. package/corpus/templates/plan/actions/consume-plan-feedback.ts +1 -9
  35. package/corpus/templates/plan/actions/convert-visual-plan-to-prototype.ts +1 -1
  36. package/corpus/templates/plan/actions/create-plan-design.ts +1 -1
  37. package/corpus/templates/plan/actions/create-prototype-plan.ts +1 -1
  38. package/corpus/templates/plan/actions/create-ui-plan.ts +1 -1
  39. package/corpus/templates/plan/actions/create-visual-plan.ts +36 -19
  40. package/corpus/templates/plan/actions/create-visual-questions.ts +1 -1
  41. package/corpus/templates/plan/actions/create-visual-recap.ts +1 -1
  42. package/corpus/templates/plan/actions/delete-plan-comment.ts +1 -9
  43. package/corpus/templates/plan/actions/delete-visual-plan.ts +1 -9
  44. package/corpus/templates/plan/actions/get-plan-feedback.ts +1 -9
  45. package/corpus/templates/plan/actions/get-visual-plan.ts +1 -9
  46. package/corpus/templates/plan/actions/import-visual-plan-source.ts +1 -1
  47. package/corpus/templates/plan/actions/list-visual-plans.ts +1 -9
  48. package/corpus/templates/plan/actions/reply-to-plan-comment.ts +1 -9
  49. package/corpus/templates/plan/actions/resolve-plan-comment.ts +1 -9
  50. package/corpus/templates/plan/actions/update-visual-plan.ts +1 -1
  51. package/corpus/templates/plan/actions/visual-answer.ts +1 -1
  52. package/corpus/templates/plan/app/components/layout/Layout.tsx +15 -0
  53. package/corpus/templates/plan/app/entry.client.tsx +10 -0
  54. package/corpus/templates/plan/app/global.css +28 -0
  55. package/corpus/templates/plan/shared/plan-content.ts +10 -6
  56. package/dist/client/frame.d.ts.map +1 -1
  57. package/dist/client/frame.js +59 -0
  58. package/dist/client/frame.js.map +1 -1
  59. package/dist/collab/routes.d.ts +1 -1
  60. package/dist/file-upload/builder.d.ts.map +1 -1
  61. package/dist/file-upload/builder.js +137 -25
  62. package/dist/file-upload/builder.js.map +1 -1
  63. package/dist/file-upload/index.d.ts +1 -1
  64. package/dist/file-upload/index.d.ts.map +1 -1
  65. package/dist/file-upload/index.js.map +1 -1
  66. package/dist/file-upload/types.d.ts +26 -0
  67. package/dist/file-upload/types.d.ts.map +1 -1
  68. package/dist/file-upload/types.js.map +1 -1
  69. package/dist/mcp/build-server.d.ts.map +1 -1
  70. package/dist/mcp/build-server.js +31 -4
  71. package/dist/mcp/build-server.js.map +1 -1
  72. package/dist/mcp/embed-app.d.ts.map +1 -1
  73. package/dist/mcp/embed-app.js +33 -18
  74. package/dist/mcp/embed-app.js.map +1 -1
  75. package/dist/notifications/routes.d.ts +3 -3
  76. package/dist/observability/routes.d.ts +3 -3
  77. package/dist/progress/routes.d.ts +1 -1
  78. package/dist/resources/handlers.d.ts +3 -3
  79. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  80. package/dist/server/transcribe-voice.d.ts +1 -1
  81. package/package.json +1 -1
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-06-30
4
+ ---
5
+
6
+ Design Studio canvas layers can be copied, pasted, and reparented more reliably across screens.
@@ -16,7 +16,9 @@ export const E2E_EMAIL = "e2e@local.test";
16
16
  export const E2E_PASSWORD = "password-e2e-1234";
17
17
  export const SEED_TITLE = "E2E Seed Design";
18
18
 
19
- const AUTH_DIR = path.join(import.meta.dirname, ".auth");
19
+ const AUTH_DIR = process.env.E2E_AUTH_DIR
20
+ ? path.resolve(process.env.E2E_AUTH_DIR)
21
+ : path.join(import.meta.dirname, ".auth");
20
22
  const STATE_PATH = path.join(AUTH_DIR, "state.json");
21
23
  const SEED_PATH = path.join(AUTH_DIR, "seed.json");
22
24
  const BROWSER_CHANNEL = process.env.E2E_BROWSER_CHANNEL;
@@ -25,7 +25,10 @@ import { FIXTURE_HTML, SEED_TITLE } from "./global-setup";
25
25
  */
26
26
 
27
27
  export async function readSeedDesignId(): Promise<string> {
28
- const seedPath = path.join(import.meta.dirname, ".auth", "seed.json");
28
+ const authDir = process.env.E2E_AUTH_DIR
29
+ ? path.resolve(process.env.E2E_AUTH_DIR)
30
+ : path.join(import.meta.dirname, ".auth");
31
+ const seedPath = path.join(authDir, "seed.json");
29
32
  const raw = await readFile(seedPath, "utf8");
30
33
  const { designId } = JSON.parse(raw) as { designId: string };
31
34
  if (!designId) throw new Error("no seeded designId - global-setup failed");
@@ -15,6 +15,9 @@ import { defineConfig, devices } from "@playwright/test";
15
15
  */
16
16
  const PORT = Number(process.env.E2E_PORT ?? 9333);
17
17
  const BASE_URL = process.env.E2E_BASE_URL ?? `http://127.0.0.1:${PORT}`;
18
+ const AUTH_DIR = process.env.E2E_AUTH_DIR
19
+ ? path.resolve(process.env.E2E_AUTH_DIR)
20
+ : path.join(import.meta.dirname, "e2e", ".auth");
18
21
  const E2E_DATABASE_URL = `file:${path.join(
19
22
  import.meta.dirname,
20
23
  "data",
@@ -34,7 +37,7 @@ export default defineConfig({
34
37
  globalSetup: path.join(import.meta.dirname, "e2e", "global-setup.ts"),
35
38
  use: {
36
39
  baseURL: BASE_URL,
37
- storageState: path.join(import.meta.dirname, "e2e", ".auth", "state.json"),
40
+ storageState: path.join(AUTH_DIR, "state.json"),
38
41
  trace: "on-first-retry",
39
42
  actionTimeout: 15_000,
40
43
  navigationTimeout: 30_000,
@@ -1,4 +1,4 @@
1
- import { defineAction, embedApp } from "@agent-native/core";
1
+ import { defineAction } from "@agent-native/core";
2
2
  import { and, eq, inArray, isNull } from "drizzle-orm";
3
3
  import { z } from "zod";
4
4
 
@@ -28,14 +28,6 @@ export default defineAction({
28
28
  },
29
29
  mcpApp: {
30
30
  compactCatalog: true,
31
- resource: embedApp({
32
- title: "Consume Feedback",
33
- description:
34
- "Open the Agent-Native Plan surface to view and manage feedback comments.",
35
- iframeTitle: "Agent-Native Plan",
36
- openLabel: "Open Plan",
37
- height: 860,
38
- }),
39
31
  },
40
32
  run: async (args) => {
41
33
  // Consuming feedback requires editor access — same as authoring changes in
@@ -58,7 +58,7 @@ export default defineAction({
58
58
  "Open the converted Agent-Native Plan prototype review surface.",
59
59
  iframeTitle: "Agent-Native Plan",
60
60
  openLabel: "Open Visual Plan",
61
- height: 860,
61
+ height: 900,
62
62
  }),
63
63
  },
64
64
  run: async (args) => {
@@ -200,7 +200,7 @@ export default defineAction({
200
200
  "Open the Agent-Native Plan design review surface for full-fidelity screens, prototype behavior, comments, and implementation notes.",
201
201
  iframeTitle: "Agent-Native Plan",
202
202
  openLabel: "Open Plan Design",
203
- height: 860,
203
+ height: 900,
204
204
  }),
205
205
  },
206
206
  run: async (args) => {
@@ -158,7 +158,7 @@ export default defineAction({
158
158
  "Open the Agent-Native Plan prototype review surface for functional states, comments, static mocks, and implementation notes.",
159
159
  iframeTitle: "Agent-Native Plan",
160
160
  openLabel: "Open Visual Plan",
161
- height: 860,
161
+ height: 900,
162
162
  }),
163
163
  },
164
164
  run: async (args) => {
@@ -127,7 +127,7 @@ export default defineAction({
127
127
  "Open the Agent-Native Plan UI review surface for sketch mockups, state annotations, implementation maps, snippets, and validation notes.",
128
128
  iframeTitle: "Agent-Native Plan",
129
129
  openLabel: "Open UI Plan",
130
- height: 860,
130
+ height: 900,
131
131
  }),
132
132
  },
133
133
  run: async (args) => {
@@ -53,7 +53,7 @@ function inferImportedPlanTitle(planText: string): string {
53
53
 
54
54
  export default defineAction({
55
55
  description:
56
- "Create a document-first structured plan for any coding task. For a plan whose centerpiece is wireframed screens/states on a canvas use create-ui-plan; for a recap of an existing diff use create-visual-recap; for a running interactive prototype use create-prototype-plan; for a full-fidelity branded design use create-plan-design. Also accepts imported Codex, Claude Code, Markdown, or pasted plan text via planText. Publish via this tool; never deliver the plan as inline chat text.",
56
+ "Create a document-first structured plan for any coding task. Call this EXACTLY ONCE per plan — produce the final plan in a single call. Do NOT create a draft and then a second 'clean' version; if you need to revise after creating, call update-visual-plan with the existing planId instead of creating another plan (each create renders its own embed, so a second create leaves a duplicate). For a plan whose centerpiece is wireframed screens/states on a canvas use create-ui-plan; for a recap of an existing diff use create-visual-recap; for a running interactive prototype use create-prototype-plan; for a full-fidelity branded design use create-plan-design. Also accepts imported Codex, Claude Code, Markdown, or pasted plan text via planText. Publish via this tool; never deliver the plan as inline chat text.",
57
57
  schema: z
58
58
  .object({
59
59
  title: z.string().optional().describe("Short plan title"),
@@ -121,7 +121,7 @@ export default defineAction({
121
121
  "Open the Agent-Native Plan review surface for structured blocks, inline diagrams, optional UI wireframes/prototypes, and comments.",
122
122
  iframeTitle: "Agent-Native Plan",
123
123
  openLabel: "Open Plan",
124
- height: 860,
124
+ height: 900,
125
125
  }),
126
126
  },
127
127
  run: async (args) => {
@@ -216,26 +216,43 @@ export default defineAction({
216
216
  visibility: "private",
217
217
  });
218
218
 
219
- await getDb()
220
- .insert(schema.planSections)
221
- .values(
222
- sections.map((section, index) => ({
223
- id: section.id ?? newId("sec"),
224
- planId: id,
225
- type: section.type,
226
- title: section.title,
227
- body: section.body,
228
- html: section.html ?? null,
229
- order: section.order ?? index,
230
- createdBy: section.createdBy,
231
- createdAt: now,
232
- updatedAt: now,
233
- })),
234
- );
219
+ // `planSections.id` is a GLOBAL primary key, so a client-supplied id (e.g.
220
+ // "section-1") collides across plans/retries and throws on insert. Always
221
+ // generate a unique server id; keep a logical-id -> row-id map so comment
222
+ // anchors can be remapped instead of failing the foreign key.
223
+ const sectionIdByLogical = new Map<string, string>();
224
+ const sectionRows = sections.map((section, index) => {
225
+ const rowId = newId("sec");
226
+ const logicalId = section.id ?? `section-${index + 1}`;
227
+ sectionIdByLogical.set(logicalId, rowId);
228
+ if (section.id) sectionIdByLogical.set(section.id, rowId);
229
+ return {
230
+ id: rowId,
231
+ planId: id,
232
+ type: section.type,
233
+ title: section.title,
234
+ body: section.body,
235
+ html: section.html ?? null,
236
+ order: section.order ?? index,
237
+ createdBy: section.createdBy,
238
+ createdAt: now,
239
+ updatedAt: now,
240
+ };
241
+ });
242
+ await getDb().insert(schema.planSections).values(sectionRows);
243
+
244
+ // Remap each comment's sectionId to its real row id; drop (undefined)
245
+ // anchors that match no section rather than failing the FK and blocking the
246
+ // publish.
247
+ const commentsForInsert = args.comments.map((comment) => {
248
+ if (!comment.sectionId) return comment;
249
+ const mapped = sectionIdByLogical.get(comment.sectionId);
250
+ return { ...comment, sectionId: mapped };
251
+ });
235
252
 
236
253
  await insertInitialPlanComments({
237
254
  planId: id,
238
- comments: args.comments,
255
+ comments: commentsForInsert,
239
256
  requestEmail: requesterEmail,
240
257
  requestName: requesterName,
241
258
  now,
@@ -149,7 +149,7 @@ export default defineAction({
149
149
  "Open an interactive visual intake form with chips, mockup options, diagrams, freeform answers, and agent handoff.",
150
150
  iframeTitle: "Agent-Native Plan",
151
151
  openLabel: "Open Visual Questions",
152
- height: 860,
152
+ height: 900,
153
153
  }),
154
154
  },
155
155
  run: async (args) => {
@@ -265,7 +265,7 @@ export default defineAction({
265
265
  "Open the Agent-Native Plan review surface for a visual code-review recap.",
266
266
  iframeTitle: "Agent-Native Plan",
267
267
  openLabel: "Open Recap",
268
- height: 860,
268
+ height: 900,
269
269
  }),
270
270
  },
271
271
  run: async (args) => {
@@ -1,4 +1,4 @@
1
- import { defineAction, embedApp } from "@agent-native/core";
1
+ import { defineAction } from "@agent-native/core";
2
2
  import { getRequestUserEmail } from "@agent-native/core/server/request-context";
3
3
  import {
4
4
  ForbiddenError,
@@ -72,14 +72,6 @@ export default defineAction({
72
72
  },
73
73
  mcpApp: {
74
74
  compactCatalog: true,
75
- resource: embedApp({
76
- title: "Delete Comment",
77
- description:
78
- "Open the Agent-Native Plan surface to manage reviewer comments.",
79
- iframeTitle: "Agent-Native Plan",
80
- openLabel: "Open Plan",
81
- height: 860,
82
- }),
83
75
  },
84
76
  run: async (args) => {
85
77
  const requesterEmail = getRequestUserEmail();
@@ -1,4 +1,4 @@
1
- import { defineAction, embedApp } from "@agent-native/core";
1
+ import { defineAction } from "@agent-native/core";
2
2
  import { deleteCollabState } from "@agent-native/core/collab";
3
3
  import { getDbExec } from "@agent-native/core/db";
4
4
  import { getRequestUserEmail } from "@agent-native/core/server/request-context";
@@ -206,14 +206,6 @@ export default defineAction({
206
206
  needsApproval: (args) => args.mode === "hard",
207
207
  mcpApp: {
208
208
  compactCatalog: true,
209
- resource: embedApp({
210
- title: "Delete Plan Data",
211
- description:
212
- "Open the Agent-Native Plan surface to manage hosted plan deletion.",
213
- iframeTitle: "Agent-Native Plan",
214
- openLabel: "Open Plan",
215
- height: 860,
216
- }),
217
209
  },
218
210
  run: async (args) => {
219
211
  const deleteOwnerEmail = requireDeleteOwnerEmail();
@@ -1,4 +1,4 @@
1
- import { defineAction, embedApp } from "@agent-native/core";
1
+ import { defineAction } from "@agent-native/core";
2
2
  import { z } from "zod";
3
3
 
4
4
  import { loadPlanBundle } from "../server/plans.js";
@@ -410,14 +410,6 @@ export default defineAction({
410
410
  },
411
411
  mcpApp: {
412
412
  compactCatalog: true,
413
- resource: embedApp({
414
- title: "Plan Feedback",
415
- description:
416
- "Open the Agent-Native Plan surface for reviewer feedback, annotations, and comments.",
417
- iframeTitle: "Agent-Native Plan",
418
- openLabel: "Open Plan Feedback",
419
- height: 860,
420
- }),
421
413
  },
422
414
  run: async (args) => {
423
415
  const bundle = await loadPlanBundle(args.planId);
@@ -1,4 +1,4 @@
1
- import { defineAction, embedApp } from "@agent-native/core";
1
+ import { defineAction } from "@agent-native/core";
2
2
  import { z } from "zod";
3
3
 
4
4
  import {
@@ -64,14 +64,6 @@ export default defineAction({
64
64
  },
65
65
  mcpApp: {
66
66
  compactCatalog: true,
67
- resource: embedApp({
68
- title: "Plan",
69
- description:
70
- "Open the Agent-Native Plan review surface for structured blocks, annotations, and comments.",
71
- iframeTitle: "Agent-Native Plan",
72
- openLabel: "Open Plan",
73
- height: 860,
74
- }),
75
67
  },
76
68
  run: async (args, ctx) => {
77
69
  const bundle = await loadPlanBundle(args.id);
@@ -91,7 +91,7 @@ export default defineAction({
91
91
  description: "Open an imported Agent-Native Plan review surface.",
92
92
  iframeTitle: "Agent-Native Plan",
93
93
  openLabel: "Open Plan",
94
- height: 860,
94
+ height: 900,
95
95
  }),
96
96
  },
97
97
  run: async (args) => {
@@ -1,4 +1,4 @@
1
- import { defineAction, embedApp } from "@agent-native/core";
1
+ import { defineAction } from "@agent-native/core";
2
2
  import { accessFilter, currentAccess } from "@agent-native/core/sharing";
3
3
  import { and, desc, eq, isNotNull, isNull, or } from "drizzle-orm";
4
4
  import { z } from "zod";
@@ -36,14 +36,6 @@ export default defineAction({
36
36
  readOnly: true,
37
37
  mcpApp: {
38
38
  compactCatalog: true,
39
- resource: embedApp({
40
- title: "Plans",
41
- description:
42
- "Open the Agent-Native Plan index for existing plans and recaps.",
43
- iframeTitle: "Agent-Native Plan",
44
- openLabel: "Open Plans",
45
- height: 860,
46
- }),
47
39
  },
48
40
  run: async (args) => {
49
41
  // Project only the columns the list/summary needs. A bare `.select()` pulls
@@ -1,4 +1,4 @@
1
- import { defineAction, embedApp } from "@agent-native/core";
1
+ import { defineAction } from "@agent-native/core";
2
2
  import {
3
3
  getRequestUserEmail,
4
4
  getRequestUserName,
@@ -63,14 +63,6 @@ export default defineAction({
63
63
  },
64
64
  mcpApp: {
65
65
  compactCatalog: true,
66
- resource: embedApp({
67
- title: "Reply to Comment",
68
- description:
69
- "Open the Agent-Native Plan surface to view and reply to comment threads.",
70
- iframeTitle: "Agent-Native Plan",
71
- openLabel: "Open Plan",
72
- height: 860,
73
- }),
74
66
  },
75
67
  run: async (args) => {
76
68
  const requesterEmail = getRequestUserEmail();
@@ -1,4 +1,4 @@
1
- import { defineAction, embedApp } from "@agent-native/core";
1
+ import { defineAction } from "@agent-native/core";
2
2
  import {
3
3
  getRequestUserEmail,
4
4
  getRequestUserName,
@@ -101,14 +101,6 @@ export default defineAction({
101
101
  },
102
102
  mcpApp: {
103
103
  compactCatalog: true,
104
- resource: embedApp({
105
- title: "Resolve Comment",
106
- description:
107
- "Open the Agent-Native Plan surface to manage comment thread statuses.",
108
- iframeTitle: "Agent-Native Plan",
109
- openLabel: "Open Plan",
110
- height: 860,
111
- }),
112
104
  },
113
105
  run: async (args) => {
114
106
  const requesterEmail = getRequestUserEmail();
@@ -456,7 +456,7 @@ export default defineAction({
456
456
  "Open the Agent-Native Plan editor for structured content, comments, and status changes.",
457
457
  iframeTitle: "Agent-Native Plan",
458
458
  openLabel: "Open Plan",
459
- height: 860,
459
+ height: 900,
460
460
  }),
461
461
  },
462
462
  run: async (args) => {
@@ -182,7 +182,7 @@ export default defineAction({
182
182
  description: "Open the Agent-Native Plan visual answer surface.",
183
183
  iframeTitle: "Agent-Native Plan",
184
184
  openLabel: "Open Visual Answer",
185
- height: 860,
185
+ height: 900,
186
186
  }),
187
187
  },
188
188
  // Render the answer's diagram/wireframe/api-spec/data-model blocks INLINE in
@@ -132,6 +132,21 @@ export function Layout({ children }: LayoutProps) {
132
132
  window.removeEventListener(PLAN_READER_VIEW_EVENT, onPlanReaderView);
133
133
  }, [planDetailRoute]);
134
134
 
135
+ // Embed mode: render just the reader, flowing — no Sidebar, no AgentSidebar,
136
+ // no h-screen shell. Those (some in shared core) lock the embed to the iframe
137
+ // height; bypassing them lets the document flow so the shell sizes to content
138
+ // (see global.css `html[data-embed]` + frame.ts content-height reporting).
139
+ const embedded = new URLSearchParams(location.search).get("embedded") === "1";
140
+ if (embedded) {
141
+ return (
142
+ <HeaderActionsProvider>
143
+ <div className="agent-embed-root flex w-full flex-col bg-background text-foreground">
144
+ {children}
145
+ </div>
146
+ </HeaderActionsProvider>
147
+ );
148
+ }
149
+
135
150
  const pageContent = (
136
151
  <div className="flex h-full flex-1 flex-col overflow-hidden">
137
152
  {ownsToolbar ? (
@@ -16,4 +16,14 @@ if (context) {
16
16
  context.basename = routerBasePath;
17
17
  }
18
18
 
19
+ // Embed mode: mark the document so the reader flows to its content height (see
20
+ // global.css `html[data-embed]`). Set before hydration so there's no flash.
21
+ try {
22
+ if (new URLSearchParams(window.location.search).get("embedded") === "1") {
23
+ document.documentElement.dataset.embed = "1";
24
+ }
25
+ } catch {
26
+ // ignore — non-embedded contexts never set the marker
27
+ }
28
+
19
29
  hydrateRoot(document, <HydratedRouter useTransitions={false} />);
@@ -2394,3 +2394,31 @@
2394
2394
  outline-offset: 2px;
2395
2395
  border-radius: 6px;
2396
2396
  }
2397
+
2398
+ /* Embed mode (set by entry.client.tsx on `?embedded=1`). The app shell is
2399
+ * fixed-viewport (`h-screen` + `overflow-hidden`), so inside the iframe
2400
+ * scrollHeight equals the iframe height and the embed can't size to content.
2401
+ * Flow the shell -> reader chain so frame.ts reports the real height. Scoped to
2402
+ * [data-embed]; covers the AgentSidebar wrapper too since the chain spans
2403
+ * several containers. */
2404
+ html[data-embed="1"],
2405
+ html[data-embed="1"] body,
2406
+ html[data-embed="1"] #root {
2407
+ height: auto !important;
2408
+ min-height: 0 !important;
2409
+ overflow: visible !important;
2410
+ }
2411
+
2412
+ html[data-embed="1"] .agent-layout-shell,
2413
+ html[data-embed="1"] .agent-layout-main-surface,
2414
+ html[data-embed="1"] .agent-sidebar-root,
2415
+ html[data-embed="1"] .agent-sidebar-content,
2416
+ html[data-embed="1"] .plans-workspace,
2417
+ html[data-embed="1"] .plan-content-surface,
2418
+ html[data-embed="1"] [data-agent-sidebar],
2419
+ html[data-embed="1"] [data-agent-sidebar-content] {
2420
+ height: auto !important;
2421
+ min-height: 0 !important;
2422
+ max-height: none !important;
2423
+ overflow: visible !important;
2424
+ }
@@ -1412,10 +1412,14 @@ export const imageDataSchema: z.ZodType<PlanImageBlock["data"]> = z
1412
1412
  message: "Image block requires an assetId or url.",
1413
1413
  });
1414
1414
 
1415
+ // Generous max lengths so a long question title/label never hard-fails a
1416
+ // publish (which forces the agent to retry and leaves an empty embed behind).
1417
+ // These are well above any realistic title/label; the renderer truncates
1418
+ // visually as needed.
1415
1419
  const planQuestionOptionSchema: z.ZodType<PlanQuestionOption> = z.object({
1416
1420
  id: idSchema,
1417
- label: z.string().trim().min(1).max(220),
1418
- detail: z.string().trim().max(800).optional(),
1421
+ label: z.string().trim().min(1).max(2000),
1422
+ detail: z.string().trim().max(4000).optional(),
1419
1423
  recommended: z.boolean().optional(),
1420
1424
  wireframe: wireframeDataSchema.optional(),
1421
1425
  diagram: diagramDataSchema.optional(),
@@ -1423,19 +1427,19 @@ const planQuestionOptionSchema: z.ZodType<PlanQuestionOption> = z.object({
1423
1427
 
1424
1428
  const planQuestionSchema: z.ZodType<PlanQuestion> = z.object({
1425
1429
  id: idSchema,
1426
- title: z.string().trim().min(1).max(260),
1427
- subtitle: z.string().trim().max(700).optional(),
1430
+ title: z.string().trim().min(1).max(2000),
1431
+ subtitle: z.string().trim().max(4000).optional(),
1428
1432
  mode: z.enum(["single", "multi", "freeform"]),
1429
1433
  options: z.array(planQuestionOptionSchema).max(40).optional(),
1430
1434
  allowOther: z.boolean().optional(),
1431
- placeholder: z.string().trim().max(240).optional(),
1435
+ placeholder: z.string().trim().max(1000).optional(),
1432
1436
  required: z.boolean().optional(),
1433
1437
  });
1434
1438
 
1435
1439
  export const questionFormDataSchema: z.ZodType<PlanQuestionFormBlock["data"]> =
1436
1440
  z.object({
1437
1441
  questions: z.array(planQuestionSchema).min(1).max(40),
1438
- submitLabel: z.string().trim().max(80).optional(),
1442
+ submitLabel: z.string().trim().max(400).optional(),
1439
1443
  });
1440
1444
 
1441
1445
  export const planBlockSchema: z.ZodType<PlanBlock> = z.lazy(() =>
@@ -1 +1 @@
1
- {"version":3,"file":"frame.d.ts","sourceRoot":"","sources":["../../src/client/frame.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAMH;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI,CAM1D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,GAC3B,MAAM,IAAI,CAWZ;AAkBD,wBAAgB,+BAA+B,IAAI,MAAM,GAAG,IAAI,CAG/D;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAiBlE;AAyBD;;;GAGG;AACH,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAE9C;AAED;;;GAGG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AA6CD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAY7D;AAMD,MAAM,WAAW,QAAQ;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,SAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoCnE;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAExD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAEvD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
1
+ {"version":3,"file":"frame.d.ts","sourceRoot":"","sources":["../../src/client/frame.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAMH;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI,CAM1D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,GAC3B,MAAM,IAAI,CAWZ;AAkBD,wBAAgB,+BAA+B,IAAI,MAAM,GAAG,IAAI,CAG/D;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAiBlE;AAgFD;;;GAGG;AACH,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAE9C;AAED;;;GAGG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AA6CD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAY7D;AAMD,MAAM,WAAW,QAAQ;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,SAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoCnE;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAExD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAEvD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
@@ -73,6 +73,64 @@ export function isTrustedFrameMessage(event) {
73
73
  return false;
74
74
  return event.source === window.parent || event.source === window;
75
75
  }
76
+ // ---------------------------------------------------------------------------
77
+ // Content-height reporting (for MCP App embeds)
78
+ // ---------------------------------------------------------------------------
79
+ //
80
+ // The embed shell renders this app in a cross-origin iframe and can't measure
81
+ // its content, so we post the real content height for the shell to size to.
82
+ // Only shrinks when the document flows to its natural height — embed layouts
83
+ // must not force `100vh`/`h-screen` on the root, or scrollHeight just equals
84
+ // the iframe height.
85
+ let _contentHeightReportingStarted = false;
86
+ let _lastReportedContentHeight = 0;
87
+ function measureContentHeight() {
88
+ if (typeof document === "undefined")
89
+ return 0;
90
+ const doc = document.documentElement;
91
+ const body = document.body;
92
+ return Math.ceil(Math.max(doc ? doc.scrollHeight : 0, body ? body.scrollHeight : 0));
93
+ }
94
+ function reportContentHeight() {
95
+ if (typeof window === "undefined" || window.parent === window)
96
+ return;
97
+ const height = measureContentHeight();
98
+ if (!height || Math.abs(height - _lastReportedContentHeight) < 2)
99
+ return;
100
+ _lastReportedContentHeight = height;
101
+ sendToFrame("agentNative.contentHeight", { height });
102
+ }
103
+ function startContentHeightReporting() {
104
+ if (_contentHeightReportingStarted || typeof window === "undefined")
105
+ return;
106
+ if (window.parent === window)
107
+ return;
108
+ _contentHeightReportingStarted = true;
109
+ let scheduled = false;
110
+ const schedule = () => {
111
+ if (scheduled)
112
+ return;
113
+ scheduled = true;
114
+ window.requestAnimationFrame(() => {
115
+ scheduled = false;
116
+ reportContentHeight();
117
+ });
118
+ };
119
+ schedule();
120
+ for (const delay of [100, 300, 800, 1500])
121
+ window.setTimeout(schedule, delay);
122
+ try {
123
+ const observer = new ResizeObserver(schedule);
124
+ if (document.documentElement)
125
+ observer.observe(document.documentElement);
126
+ if (document.body)
127
+ observer.observe(document.body);
128
+ }
129
+ catch {
130
+ window.addEventListener("resize", schedule, { passive: true });
131
+ }
132
+ window.addEventListener("load", schedule, { passive: true });
133
+ }
76
134
  // Listen for frame origin message and cache it.
77
135
  // Only accept from the direct parent frame, and only set once.
78
136
  if (typeof window !== "undefined") {
@@ -87,6 +145,7 @@ if (typeof window !== "undefined") {
87
145
  event.source === window.parent) {
88
146
  _frameOrigin = origin;
89
147
  window.parent.postMessage({ type: "agentNative.embeddedAppReady" }, getFramePostMessageTargetOrigin() ?? window.location.origin);
148
+ startContentHeightReporting();
90
149
  }
91
150
  });
92
151
  }