@agent-native/core 0.98.1 → 0.98.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.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +8 -0
- package/corpus/core/docs/content/locales/ar-SA/pr-visual-recap.mdx +25 -1
- package/corpus/core/docs/content/locales/ar-SA/template-plan.mdx +1 -1
- package/corpus/core/docs/content/locales/de-DE/pr-visual-recap.mdx +25 -1
- package/corpus/core/docs/content/locales/de-DE/template-plan.mdx +1 -1
- package/corpus/core/docs/content/locales/es-ES/pr-visual-recap.mdx +25 -1
- package/corpus/core/docs/content/locales/es-ES/template-plan.mdx +1 -1
- package/corpus/core/docs/content/locales/fr-FR/pr-visual-recap.mdx +25 -1
- package/corpus/core/docs/content/locales/fr-FR/template-plan.mdx +1 -1
- package/corpus/core/docs/content/locales/hi-IN/pr-visual-recap.mdx +25 -1
- package/corpus/core/docs/content/locales/hi-IN/template-plan.mdx +1 -1
- package/corpus/core/docs/content/locales/ja-JP/pr-visual-recap.mdx +25 -1
- package/corpus/core/docs/content/locales/ja-JP/template-plan.mdx +1 -1
- package/corpus/core/docs/content/locales/ko-KR/pr-visual-recap.mdx +25 -1
- package/corpus/core/docs/content/locales/ko-KR/template-plan.mdx +1 -1
- package/corpus/core/docs/content/locales/pt-BR/pr-visual-recap.mdx +25 -1
- package/corpus/core/docs/content/locales/pt-BR/template-plan.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-CN/pr-visual-recap.mdx +25 -1
- package/corpus/core/docs/content/locales/zh-CN/template-plan.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-TW/pr-visual-recap.mdx +25 -1
- package/corpus/core/docs/content/locales/zh-TW/template-plan.mdx +1 -1
- package/corpus/core/docs/content/pr-visual-recap.mdx +50 -1
- package/corpus/core/docs/content/template-plan.mdx +1 -1
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/cli/plan-local.ts +43 -11
- package/corpus/core/src/cli/pr-visual-recap-workflow.ts +2 -9
- package/corpus/core/src/cli/recap.ts +356 -4
- package/corpus/core/src/cli/skills-content/local-files.ts +17 -12
- package/corpus/core/src/client/analytics.ts +96 -14
- package/corpus/core/src/client/index.ts +5 -0
- package/corpus/core/src/client/session-replay.ts +10 -0
- package/corpus/core/src/client/settings/DemoModeSection.tsx +4 -28
- package/corpus/core/src/client/url-scrub.ts +13 -0
- package/corpus/core/src/client/use-demo-mode-status.ts +44 -0
- package/corpus/templates/analytics/AGENTS.md +8 -0
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +18 -4
- package/corpus/templates/analytics/app/lib/demo-chart-trend.ts +156 -0
- package/corpus/templates/analytics/changelog/2026-07-12-daily-dashboard-emails-reliably-include-the-complete-dashboa.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-12-demo-mode-charts-trend-upward.md +6 -0
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +2 -0
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +17 -33
- package/corpus/templates/clips/app/components/player/video-player.tsx +5 -1
- package/corpus/templates/clips/changelog/2026-07-11-clips-no-longer-stay-stuck-on-preparing-clip-when-they-are-r.md +6 -0
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +2 -2
- package/corpus/templates/content/changelog/2026-07-12-the-left-sidebar-stays-fixed-horizontally-while-scrolling-on.md +6 -0
- package/corpus/templates/plan/.agents/skills/visual-plan/references/local-files.md +17 -12
- package/corpus/templates/plan/.agents/skills/visual-recap/references/local-files.md +17 -12
- package/corpus/templates/plan/actions/validate-local-plan-source.ts +4 -4
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +2 -1
- package/corpus/templates/plan/app/lib/plan-local-bridge.ts +41 -0
- package/corpus/templates/plan/app/lib/plan-tracking.ts +3 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +9 -9
- package/corpus/templates/plan/app/root.tsx +13 -3
- package/corpus/templates/plan/changelog/2026-07-12-local-plan-source-now-stays-on-device.md +6 -0
- package/dist/cli/plan-local.d.ts +6 -6
- package/dist/cli/plan-local.d.ts.map +1 -1
- package/dist/cli/plan-local.js +36 -7
- package/dist/cli/plan-local.js.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts +2 -8
- package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.js +2 -8
- package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
- package/dist/cli/recap.d.ts +22 -1
- package/dist/cli/recap.d.ts.map +1 -1
- package/dist/cli/recap.js +286 -2
- package/dist/cli/recap.js.map +1 -1
- package/dist/cli/skills-content/local-files.d.ts +1 -1
- package/dist/cli/skills-content/local-files.d.ts.map +1 -1
- package/dist/cli/skills-content/local-files.js +17 -12
- package/dist/cli/skills-content/local-files.js.map +1 -1
- package/dist/client/analytics.d.ts +8 -0
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +77 -13
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/index.d.ts +2 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/session-replay.d.ts +2 -0
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +7 -0
- package/dist/client/session-replay.js.map +1 -1
- package/dist/client/settings/DemoModeSection.d.ts.map +1 -1
- package/dist/client/settings/DemoModeSection.js +4 -23
- package/dist/client/settings/DemoModeSection.js.map +1 -1
- package/dist/client/url-scrub.d.ts.map +1 -1
- package/dist/client/url-scrub.js +14 -0
- package/dist/client/url-scrub.js.map +1 -1
- package/dist/client/use-demo-mode-status.d.ts +12 -0
- package/dist/client/use-demo-mode-status.d.ts.map +1 -0
- package/dist/client/use-demo-mode-status.js +30 -0
- package/dist/client/use-demo-mode-status.js.map +1 -0
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/secrets/routes.d.ts +9 -9
- package/docs/content/locales/ar-SA/pr-visual-recap.mdx +25 -1
- package/docs/content/locales/ar-SA/template-plan.mdx +1 -1
- package/docs/content/locales/de-DE/pr-visual-recap.mdx +25 -1
- package/docs/content/locales/de-DE/template-plan.mdx +1 -1
- package/docs/content/locales/es-ES/pr-visual-recap.mdx +25 -1
- package/docs/content/locales/es-ES/template-plan.mdx +1 -1
- package/docs/content/locales/fr-FR/pr-visual-recap.mdx +25 -1
- package/docs/content/locales/fr-FR/template-plan.mdx +1 -1
- package/docs/content/locales/hi-IN/pr-visual-recap.mdx +25 -1
- package/docs/content/locales/hi-IN/template-plan.mdx +1 -1
- package/docs/content/locales/ja-JP/pr-visual-recap.mdx +25 -1
- package/docs/content/locales/ja-JP/template-plan.mdx +1 -1
- package/docs/content/locales/ko-KR/pr-visual-recap.mdx +25 -1
- package/docs/content/locales/ko-KR/template-plan.mdx +1 -1
- package/docs/content/locales/pt-BR/pr-visual-recap.mdx +25 -1
- package/docs/content/locales/pt-BR/template-plan.mdx +1 -1
- package/docs/content/locales/zh-CN/pr-visual-recap.mdx +25 -1
- package/docs/content/locales/zh-CN/template-plan.mdx +1 -1
- package/docs/content/locales/zh-TW/pr-visual-recap.mdx +25 -1
- package/docs/content/locales/zh-TW/template-plan.mdx +1 -1
- package/docs/content/pr-visual-recap.mdx +50 -1
- package/docs/content/template-plan.mdx +1 -1
- package/package.json +1 -1
|
@@ -42,9 +42,8 @@ const DASHBOARD_REPORT_SCREENSHOT_PARAM = "reportScreenshot";
|
|
|
42
42
|
const DASHBOARD_REPORT_SETTINGS_PARAM = "reportSettings";
|
|
43
43
|
const DASHBOARD_REPORT_CID = "dashboard-report-snapshot";
|
|
44
44
|
const LOCAL_SCREENSHOT_TIMEOUT_MS = 90_000;
|
|
45
|
-
const SERVERLESS_SCREENSHOT_TIMEOUT_MS =
|
|
46
|
-
const SERVERLESS_SECOND_READY_TIMEOUT_MS =
|
|
47
|
-
const MAX_SCREENSHOT_VIEWPORT_HEIGHT = 30_000;
|
|
45
|
+
const SERVERLESS_SCREENSHOT_TIMEOUT_MS = 90_000;
|
|
46
|
+
const SERVERLESS_SECOND_READY_TIMEOUT_MS = 45_000;
|
|
48
47
|
const SCREENSHOT_VIEWPORT_PADDING = 64;
|
|
49
48
|
|
|
50
49
|
type DashboardScreenshotAttempt = {
|
|
@@ -328,42 +327,27 @@ async function scrollDashboardForLazyRendering(page: any): Promise<void> {
|
|
|
328
327
|
})()`);
|
|
329
328
|
}
|
|
330
329
|
|
|
331
|
-
async function
|
|
330
|
+
async function fitViewportWidthToDashboardCapture(
|
|
332
331
|
page: any,
|
|
333
332
|
capture: any,
|
|
334
|
-
|
|
333
|
+
viewport: { width: number; height: number },
|
|
335
334
|
): Promise<void> {
|
|
336
335
|
const box = await capture.boundingBox();
|
|
337
336
|
if (!box) return;
|
|
338
337
|
|
|
339
|
-
const
|
|
340
|
-
width
|
|
341
|
-
|
|
342
|
-
Math.min(1800, Math.ceil(box.width + SCREENSHOT_VIEWPORT_PADDING)),
|
|
343
|
-
),
|
|
344
|
-
height: Math.max(
|
|
345
|
-
minViewport.height,
|
|
346
|
-
Math.min(
|
|
347
|
-
MAX_SCREENSHOT_VIEWPORT_HEIGHT,
|
|
348
|
-
Math.ceil(box.height + SCREENSHOT_VIEWPORT_PADDING),
|
|
349
|
-
),
|
|
350
|
-
),
|
|
351
|
-
};
|
|
352
|
-
await page.setViewportSize(size);
|
|
353
|
-
await page.waitForTimeout(250);
|
|
354
|
-
|
|
355
|
-
const resizedBox = await capture.boundingBox();
|
|
356
|
-
if (!resizedBox) return;
|
|
357
|
-
const resizedHeight = Math.ceil(
|
|
358
|
-
resizedBox.height + SCREENSHOT_VIEWPORT_PADDING,
|
|
338
|
+
const width = Math.max(
|
|
339
|
+
viewport.width,
|
|
340
|
+
Math.min(1800, Math.ceil(box.width + SCREENSHOT_VIEWPORT_PADDING)),
|
|
359
341
|
);
|
|
360
|
-
if (
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
342
|
+
if (width === viewport.width) return;
|
|
343
|
+
|
|
344
|
+
// Keep the render surface bounded. Playwright's locator screenshot captures
|
|
345
|
+
// the full element beyond the viewport, while growing Chromium to the full
|
|
346
|
+
// dashboard height can exhaust memory and close the browser on serverless
|
|
347
|
+
// workers. Width-only fitting preserves the full dashboard without that
|
|
348
|
+
// oversized render surface.
|
|
349
|
+
await page.setViewportSize({ width, height: viewport.height });
|
|
350
|
+
await page.waitForTimeout(250);
|
|
367
351
|
}
|
|
368
352
|
|
|
369
353
|
async function captureDashboardPng(
|
|
@@ -412,7 +396,7 @@ async function captureDashboardPng(
|
|
|
412
396
|
await scrollDashboardForLazyRendering(page);
|
|
413
397
|
await waitForDashboardReportReady(page, secondReadyTimeoutMs());
|
|
414
398
|
|
|
415
|
-
await
|
|
399
|
+
await fitViewportWidthToDashboardCapture(page, capture, attempt.viewport);
|
|
416
400
|
await capture.scrollIntoViewIfNeeded();
|
|
417
401
|
const image = await capture.screenshot({
|
|
418
402
|
type: "png",
|
|
@@ -1083,13 +1083,17 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
|
1083
1083
|
const fullscreenMenuContainer = isFullscreen ? containerRef.current : null;
|
|
1084
1084
|
|
|
1085
1085
|
const showThroughoutCta = cta && cta.placement === "throughout";
|
|
1086
|
+
// Mobile Safari may defer loadeddata/canplay until playback starts. Keep
|
|
1087
|
+
// the paused state actionable even when those readiness events have not
|
|
1088
|
+
// fired yet; once the user asks to play, the pending/buffering states give
|
|
1089
|
+
// them accurate loading feedback.
|
|
1086
1090
|
const centerOverlayMode =
|
|
1087
1091
|
activeVideoSrc &&
|
|
1088
1092
|
!isLoomEmbed &&
|
|
1089
1093
|
!unsupportedFormat &&
|
|
1090
1094
|
!showEndCta &&
|
|
1091
1095
|
(!isPlaying || isPlayPending || isBuffering)
|
|
1092
|
-
?
|
|
1096
|
+
? isPlayPending || isBuffering
|
|
1093
1097
|
? "loading"
|
|
1094
1098
|
: "ready"
|
|
1095
1099
|
: null;
|
|
@@ -1188,8 +1188,8 @@ export function DocumentSidebar({
|
|
|
1188
1188
|
</div>
|
|
1189
1189
|
)}
|
|
1190
1190
|
|
|
1191
|
-
<ScrollArea className="min-h-0 flex-1">
|
|
1192
|
-
<div className="
|
|
1191
|
+
<ScrollArea className="min-h-0 flex-1 [&_[data-radix-scroll-area-viewport]]:!overflow-x-hidden">
|
|
1192
|
+
<div className="w-full min-w-0 py-2 pe-2">
|
|
1193
1193
|
{/* Search results */}
|
|
1194
1194
|
{filteredDocuments ? (
|
|
1195
1195
|
<>
|
|
@@ -36,9 +36,9 @@ The local-files contract:
|
|
|
36
36
|
— encode multiline code as JSON string attributes such as `code={"const x =\n y"}`
|
|
37
37
|
(a static template literal is accepted only when it has no `${...}`
|
|
38
38
|
interpolation). `plan local check` is a quick OFFLINE lint (a subset of the
|
|
39
|
-
renderer schema), so a green `check` does not guarantee the plan renders
|
|
40
|
-
`plan local verify`
|
|
41
|
-
|
|
39
|
+
renderer schema), so a green `check` does not guarantee the plan renders.
|
|
40
|
+
`plan local verify` also stays on-device: it uses the offline lint unless it
|
|
41
|
+
can reach a Plan renderer on an explicit loopback `--app-url`.
|
|
42
42
|
- **Write a local MDX folder.** Use `plans/<slug>/` to check the artifact into the
|
|
43
43
|
repo, or a repo-ignored/temporary folder such as `.agent-native/plans/<slug>/`
|
|
44
44
|
or `/tmp/agent-native-plans/<slug>/` when it should not be checked in. The
|
|
@@ -53,7 +53,10 @@ The local-files contract:
|
|
|
53
53
|
(use `--kind plan` for plans, `--kind recap` for recaps). Report the local
|
|
54
54
|
bridge URL from stdout or `<plan-dir>/.plan-url`; treat `.plan-url` as a local
|
|
55
55
|
token file and do not commit it. The URL opens the hosted Plan UI but reads from
|
|
56
|
-
the localhost bridge on this machine, so it is not shareable across machines.
|
|
56
|
+
the localhost bridge on this machine, so it is not shareable across machines.
|
|
57
|
+
The token is carried in the URL fragment (which is not sent to the hosted
|
|
58
|
+
origin), and the local-plan route disables DOM autocapture and session replay
|
|
59
|
+
while retaining sanitized pageviews and error monitoring. On
|
|
57
60
|
macOS `--open` prefers Chromium browsers; if Safari opens, switch to
|
|
58
61
|
Chrome/Chromium because Safari can block the hosted HTTPS page from fetching the
|
|
59
62
|
HTTP localhost bridge. If the Plan app itself is running locally with the same
|
|
@@ -63,14 +66,16 @@ The local-files contract:
|
|
|
63
66
|
running local Plan app.
|
|
64
67
|
- **Headless verify.** Run
|
|
65
68
|
`npx @agent-native/core@latest plan local verify --dir <plan-dir> --kind <plan|recap>`.
|
|
66
|
-
It starts the bridge
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
It starts the bridge and checks the private-network preflight and JSON payload
|
|
70
|
+
entirely on loopback. It never sends MDX or assets to a remote validation
|
|
71
|
+
action. When `--app-url` points to a loopback Plan app, verify also validates
|
|
72
|
+
against that local app's real renderer schema via `validate-local-plan-source`.
|
|
73
|
+
A non-`ok` result with
|
|
69
74
|
`validation.valid: false` lists the renderer's exact schema-path issues (e.g.
|
|
70
75
|
`blocks[1].data.tabs[0]...`); fix those before handing off. If `validation.ran`
|
|
71
|
-
is `false`,
|
|
72
|
-
|
|
73
|
-
|
|
76
|
+
is `false`, verify used the offline lint because the app URL was remote or the
|
|
77
|
+
local Plan app was unavailable. Run a local Plan app and pass
|
|
78
|
+
`--app-url http://localhost:8096` for the authoritative check. If the browser hangs on
|
|
74
79
|
"Loading plan", fetch the `bridgeUrl` from the verify/serve JSON to read the
|
|
75
80
|
concrete validation error.
|
|
76
81
|
- **Never call hosted tools for that plan/recap.** Do not call
|
|
@@ -86,8 +91,8 @@ The local-files contract:
|
|
|
86
91
|
attachment, and publish/export receipts are unavailable until the user
|
|
87
92
|
explicitly opts into publishing.
|
|
88
93
|
|
|
89
|
-
Local-files mode
|
|
90
|
-
Plan database. It does not by itself make the coding agent's language model local;
|
|
94
|
+
Local-files mode prevents plan/recap content from being uploaded to the
|
|
95
|
+
Agent-Native Plan server or database. It does not by itself make the coding agent's language model local;
|
|
91
96
|
for that stronger boundary the host agent/model must also be local or otherwise
|
|
92
97
|
approved by the user.
|
|
93
98
|
|
|
@@ -36,9 +36,9 @@ The local-files contract:
|
|
|
36
36
|
— encode multiline code as JSON string attributes such as `code={"const x =\n y"}`
|
|
37
37
|
(a static template literal is accepted only when it has no `${...}`
|
|
38
38
|
interpolation). `plan local check` is a quick OFFLINE lint (a subset of the
|
|
39
|
-
renderer schema), so a green `check` does not guarantee the plan renders
|
|
40
|
-
`plan local verify`
|
|
41
|
-
|
|
39
|
+
renderer schema), so a green `check` does not guarantee the plan renders.
|
|
40
|
+
`plan local verify` also stays on-device: it uses the offline lint unless it
|
|
41
|
+
can reach a Plan renderer on an explicit loopback `--app-url`.
|
|
42
42
|
- **Write a local MDX folder.** Use `plans/<slug>/` to check the artifact into the
|
|
43
43
|
repo, or a repo-ignored/temporary folder such as `.agent-native/plans/<slug>/`
|
|
44
44
|
or `/tmp/agent-native-plans/<slug>/` when it should not be checked in. The
|
|
@@ -53,7 +53,10 @@ The local-files contract:
|
|
|
53
53
|
(use `--kind plan` for plans, `--kind recap` for recaps). Report the local
|
|
54
54
|
bridge URL from stdout or `<plan-dir>/.plan-url`; treat `.plan-url` as a local
|
|
55
55
|
token file and do not commit it. The URL opens the hosted Plan UI but reads from
|
|
56
|
-
the localhost bridge on this machine, so it is not shareable across machines.
|
|
56
|
+
the localhost bridge on this machine, so it is not shareable across machines.
|
|
57
|
+
The token is carried in the URL fragment (which is not sent to the hosted
|
|
58
|
+
origin), and the local-plan route disables DOM autocapture and session replay
|
|
59
|
+
while retaining sanitized pageviews and error monitoring. On
|
|
57
60
|
macOS `--open` prefers Chromium browsers; if Safari opens, switch to
|
|
58
61
|
Chrome/Chromium because Safari can block the hosted HTTPS page from fetching the
|
|
59
62
|
HTTP localhost bridge. If the Plan app itself is running locally with the same
|
|
@@ -63,14 +66,16 @@ The local-files contract:
|
|
|
63
66
|
running local Plan app.
|
|
64
67
|
- **Headless verify.** Run
|
|
65
68
|
`npx @agent-native/core@latest plan local verify --dir <plan-dir> --kind <plan|recap>`.
|
|
66
|
-
It starts the bridge
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
It starts the bridge and checks the private-network preflight and JSON payload
|
|
70
|
+
entirely on loopback. It never sends MDX or assets to a remote validation
|
|
71
|
+
action. When `--app-url` points to a loopback Plan app, verify also validates
|
|
72
|
+
against that local app's real renderer schema via `validate-local-plan-source`.
|
|
73
|
+
A non-`ok` result with
|
|
69
74
|
`validation.valid: false` lists the renderer's exact schema-path issues (e.g.
|
|
70
75
|
`blocks[1].data.tabs[0]...`); fix those before handing off. If `validation.ran`
|
|
71
|
-
is `false`,
|
|
72
|
-
|
|
73
|
-
|
|
76
|
+
is `false`, verify used the offline lint because the app URL was remote or the
|
|
77
|
+
local Plan app was unavailable. Run a local Plan app and pass
|
|
78
|
+
`--app-url http://localhost:8096` for the authoritative check. If the browser hangs on
|
|
74
79
|
"Loading plan", fetch the `bridgeUrl` from the verify/serve JSON to read the
|
|
75
80
|
concrete validation error.
|
|
76
81
|
- **Never call hosted tools for that plan/recap.** Do not call
|
|
@@ -86,8 +91,8 @@ The local-files contract:
|
|
|
86
91
|
attachment, and publish/export receipts are unavailable until the user
|
|
87
92
|
explicitly opts into publishing.
|
|
88
93
|
|
|
89
|
-
Local-files mode
|
|
90
|
-
Plan database. It does not by itself make the coding agent's language model local;
|
|
94
|
+
Local-files mode prevents plan/recap content from being uploaded to the
|
|
95
|
+
Agent-Native Plan server or database. It does not by itself make the coding agent's language model local;
|
|
91
96
|
for that stronger boundary the host agent/model must also be local or otherwise
|
|
92
97
|
approved by the user.
|
|
93
98
|
|
|
@@ -13,8 +13,8 @@ import { parsePlanMdxFolder, planMdxFileSchema } from "../server/plan-mdx.js";
|
|
|
13
13
|
*
|
|
14
14
|
* It is intentionally a pure parse: it accepts the posted MDX folder, never
|
|
15
15
|
* reads `schema.plans`, never touches the filesystem, and never writes to the
|
|
16
|
-
* database.
|
|
17
|
-
*
|
|
16
|
+
* database. The local-files CLI calls it only on an explicit loopback Plan app;
|
|
17
|
+
* it never posts private source to this action on a hosted deployment.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
// Covers the CLI's 10 MiB raw asset cap (~13.3 MiB base64) plus MDX text, so
|
|
@@ -63,7 +63,7 @@ function toValidationIssues(error: unknown): ValidationIssue[] {
|
|
|
63
63
|
|
|
64
64
|
export default defineAction({
|
|
65
65
|
description:
|
|
66
|
-
"Validate a
|
|
66
|
+
"Validate a plan MDX folder against this Plan app's renderer schema (parsePlanMdxFolder + planContentSchema). Returns { valid, issues } where issues carry the renderer's exact schema path (e.g. blocks[1].data.items[0].id). The local-files CLI calls this only on loopback Plan apps so private source stays on-device. Pure parse: no database, filesystem, or schema.plans access.",
|
|
67
67
|
schema: z.object({
|
|
68
68
|
mdx: planMdxFileSchema.describe(
|
|
69
69
|
"The plan MDX folder: plan.mdx plus optional canvas.mdx, prototype.mdx, .plan-state.json, and assets/.",
|
|
@@ -81,7 +81,7 @@ export default defineAction({
|
|
|
81
81
|
requiresAuth: false,
|
|
82
82
|
title: "Validate Local Plan Source",
|
|
83
83
|
description:
|
|
84
|
-
"Validate a
|
|
84
|
+
"Validate a plan MDX folder against this app's renderer schema without touching the database.",
|
|
85
85
|
},
|
|
86
86
|
run: async (args) => {
|
|
87
87
|
let totalBytes = 0;
|
|
@@ -62,6 +62,7 @@ import {
|
|
|
62
62
|
} from "@/components/ui/tooltip";
|
|
63
63
|
import { usePlans } from "@/hooks/use-plans";
|
|
64
64
|
import { APP_TITLE } from "@/lib/app-config";
|
|
65
|
+
import { planReturnPathFromLocation } from "@/lib/plan-local-bridge";
|
|
65
66
|
import { cn } from "@/lib/utils";
|
|
66
67
|
|
|
67
68
|
const PLAN_CHAT_STORAGE_KEY = "plans";
|
|
@@ -651,7 +652,7 @@ export function Sidebar({
|
|
|
651
652
|
const location = useLocation();
|
|
652
653
|
const { session, isLoading: sessionLoading } = useSession();
|
|
653
654
|
const t = useT();
|
|
654
|
-
const returnPath =
|
|
655
|
+
const returnPath = planReturnPathFromLocation(location);
|
|
655
656
|
const ToggleIcon = collapsed
|
|
656
657
|
? IconLayoutSidebarLeftExpand
|
|
657
658
|
: IconLayoutSidebarLeftCollapse;
|
|
@@ -67,6 +67,47 @@ export class LocalPlanBridgePermissionError extends Error {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
type LocalPlanBridgeSessionStorage = Pick<Storage, "getItem" | "setItem">;
|
|
71
|
+
|
|
72
|
+
function browserSessionStorage(): LocalPlanBridgeSessionStorage | null {
|
|
73
|
+
if (typeof window === "undefined") return null;
|
|
74
|
+
try {
|
|
75
|
+
return window.sessionStorage;
|
|
76
|
+
} catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function localPlanBridgeUrlFromLocation(
|
|
82
|
+
hash: string,
|
|
83
|
+
slug: string,
|
|
84
|
+
storage: LocalPlanBridgeSessionStorage | null = browserSessionStorage(),
|
|
85
|
+
): string | null {
|
|
86
|
+
const hashParams = hash.startsWith("#bridge=")
|
|
87
|
+
? new URLSearchParams(hash.slice(1))
|
|
88
|
+
: null;
|
|
89
|
+
const bridgeUrl = hashParams?.get("bridge") ?? null;
|
|
90
|
+
const storageKey = `agent-native.local-plan-bridge.${slug}`;
|
|
91
|
+
try {
|
|
92
|
+
if (bridgeUrl) {
|
|
93
|
+
storage?.setItem(storageKey, bridgeUrl);
|
|
94
|
+
return bridgeUrl;
|
|
95
|
+
}
|
|
96
|
+
return storage?.getItem(storageKey) ?? null;
|
|
97
|
+
} catch {
|
|
98
|
+
return bridgeUrl;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function planReturnPathFromLocation(location: {
|
|
103
|
+
pathname: string;
|
|
104
|
+
search: string;
|
|
105
|
+
hash: string;
|
|
106
|
+
}): string {
|
|
107
|
+
const safeHash = location.hash.startsWith("#bridge=") ? "" : location.hash;
|
|
108
|
+
return `${location.pathname}${location.search}${safeHash}`;
|
|
109
|
+
}
|
|
110
|
+
|
|
70
111
|
export async function localNetworkAccessPermissionState(): Promise<
|
|
71
112
|
Exclude<LocalNetworkAccessPermissionState, "checking">
|
|
72
113
|
> {
|
|
@@ -273,11 +273,13 @@ import { planDocumentTitle } from "@/lib/plan-document-title";
|
|
|
273
273
|
import {
|
|
274
274
|
fetchLocalPlanBridgeBundle,
|
|
275
275
|
localNetworkAccessPermissionState,
|
|
276
|
+
localPlanBridgeUrlFromLocation,
|
|
276
277
|
localPlanBridgeQueryKey,
|
|
277
278
|
localPlanBridgeRetryDelay,
|
|
278
279
|
localPlanRoutePath,
|
|
279
280
|
localPlanRouteUrl,
|
|
280
281
|
mergeLocalBridgeComments,
|
|
282
|
+
planReturnPathFromLocation,
|
|
281
283
|
shouldRetryLocalPlanBridgeBundle,
|
|
282
284
|
shouldShowLocalPlanLoadError,
|
|
283
285
|
shouldShowPlanLoadError,
|
|
@@ -2024,9 +2026,10 @@ export function PlansPage({ localPlanSlug }: { localPlanSlug?: string } = {}) {
|
|
|
2024
2026
|
() => new URLSearchParams(location.search),
|
|
2025
2027
|
[location.search],
|
|
2026
2028
|
);
|
|
2027
|
-
const localPlanBridgeUrl =
|
|
2028
|
-
|
|
2029
|
-
|
|
2029
|
+
const localPlanBridgeUrl =
|
|
2030
|
+
localPlanMode && localPlanSlug
|
|
2031
|
+
? localPlanBridgeUrlFromLocation(location.hash, localPlanSlug)
|
|
2032
|
+
: null;
|
|
2030
2033
|
const localPlanRepoPath = localPlanMode
|
|
2031
2034
|
? routeSearchParams.get("path")
|
|
2032
2035
|
: null;
|
|
@@ -2137,8 +2140,7 @@ export function PlansPage({ localPlanSlug }: { localPlanSlug?: string } = {}) {
|
|
|
2137
2140
|
// Redirect to sign-in, returning to wherever the guest currently is.
|
|
2138
2141
|
const openSignIn = useCallback((returnOverride?: string) => {
|
|
2139
2142
|
const returnPath =
|
|
2140
|
-
returnOverride ??
|
|
2141
|
-
window.location.pathname + window.location.search + window.location.hash;
|
|
2143
|
+
returnOverride ?? planReturnPathFromLocation(window.location);
|
|
2142
2144
|
window.location.href = `${agentNativePath(
|
|
2143
2145
|
"/_agent-native/sign-in",
|
|
2144
2146
|
)}?return=${encodeURIComponent(returnPath)}`;
|
|
@@ -2292,8 +2294,7 @@ export function PlansPage({ localPlanSlug }: { localPlanSlug?: string } = {}) {
|
|
|
2292
2294
|
}
|
|
2293
2295
|
}, [accessRequestSentPlanId, selectedId]);
|
|
2294
2296
|
const startGoogleSignIn = useCallback(async () => {
|
|
2295
|
-
const returnPath =
|
|
2296
|
-
window.location.pathname + window.location.search + window.location.hash;
|
|
2297
|
+
const returnPath = planReturnPathFromLocation(window.location);
|
|
2297
2298
|
try {
|
|
2298
2299
|
const res = await fetch(
|
|
2299
2300
|
`${agentNativePath("/_agent-native/google/auth-url")}?return=${encodeURIComponent(returnPath)}`,
|
|
@@ -6826,8 +6827,7 @@ function PlanLoadError({
|
|
|
6826
6827
|
? t("plansPage.loadError.orgTitle", { orgName })
|
|
6827
6828
|
: null;
|
|
6828
6829
|
|
|
6829
|
-
const returnPath = () =>
|
|
6830
|
-
window.location.pathname + window.location.search + window.location.hash;
|
|
6830
|
+
const returnPath = () => planReturnPathFromLocation(window.location);
|
|
6831
6831
|
|
|
6832
6832
|
const readAuthError = async (res: Response, fallback: string) => {
|
|
6833
6833
|
const data = (await res.json().catch(() => null)) as {
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
} from "@tabler/icons-react";
|
|
21
21
|
import { useQueryClient } from "@tanstack/react-query";
|
|
22
22
|
import { useTheme } from "next-themes";
|
|
23
|
-
import { useCallback, useState } from "react";
|
|
23
|
+
import { useCallback, useEffect, useState } from "react";
|
|
24
24
|
import {
|
|
25
25
|
Links,
|
|
26
26
|
Meta,
|
|
@@ -44,13 +44,17 @@ import { useNavigationState } from "@/hooks/use-navigation-state";
|
|
|
44
44
|
// their diagram/wireframe/api-spec blocks inline in the agent chat.
|
|
45
45
|
import "@/lib/register-chat-renderers";
|
|
46
46
|
import { APP_TITLE } from "@/lib/app-config";
|
|
47
|
+
import { shouldCapturePlanContent } from "@/lib/plan-tracking";
|
|
47
48
|
import { TAB_ID } from "@/lib/tab-id";
|
|
48
49
|
|
|
49
50
|
import changelog from "../CHANGELOG.md?raw";
|
|
50
51
|
import { i18nCatalog } from "./i18n";
|
|
51
52
|
|
|
52
53
|
import stylesheet from "./global.css?url";
|
|
54
|
+
// Keep standard pageviews, explicit analytics, and Sentry on local-plan routes,
|
|
55
|
+
// but disable DOM/session capture so rendered plan contents stay on-device.
|
|
53
56
|
configureTracking({
|
|
57
|
+
contentCaptureForPath: shouldCapturePlanContent,
|
|
54
58
|
getDefaultProps: (_name, properties) => ({
|
|
55
59
|
...properties,
|
|
56
60
|
app: "plan",
|
|
@@ -221,6 +225,7 @@ export default function Root() {
|
|
|
221
225
|
location.pathname.startsWith("/recaps/") ||
|
|
222
226
|
location.pathname === "/local-plans" ||
|
|
223
227
|
location.pathname.startsWith("/local-plans/");
|
|
228
|
+
const localPlanPrivacyRoute = !shouldCapturePlanContent(location.pathname);
|
|
224
229
|
return (
|
|
225
230
|
// Pass the plan-specific styled Toaster via `toaster` so only one sonner
|
|
226
231
|
// instance renders (avoids the duplicate that would appear if AppProviders'
|
|
@@ -232,8 +237,13 @@ export default function Root() {
|
|
|
232
237
|
toaster={<Toaster richColors position="bottom-left" />}
|
|
233
238
|
i18n={{ catalog: i18nCatalog }}
|
|
234
239
|
>
|
|
235
|
-
<
|
|
236
|
-
|
|
240
|
+
<div
|
|
241
|
+
data-an-mask={localPlanPrivacyRoute ? "" : undefined}
|
|
242
|
+
style={{ display: "contents" }}
|
|
243
|
+
>
|
|
244
|
+
<DbSyncSetup />
|
|
245
|
+
<AppContent />
|
|
246
|
+
</div>
|
|
237
247
|
</AppProviders>
|
|
238
248
|
</AppToolkitProvider>
|
|
239
249
|
);
|
package/dist/cli/plan-local.d.ts
CHANGED
|
@@ -140,7 +140,7 @@ export type RendererValidationIssue = {
|
|
|
140
140
|
message: string;
|
|
141
141
|
};
|
|
142
142
|
export type RendererValidation = {
|
|
143
|
-
/** Whether
|
|
143
|
+
/** Whether a loopback renderer validate endpoint was reachable and answered. */
|
|
144
144
|
ran: boolean;
|
|
145
145
|
endpoint: string;
|
|
146
146
|
/** Renderer verdict — present only when `ran` is true. */
|
|
@@ -211,13 +211,13 @@ export declare function startLocalPlanBridge(input: {
|
|
|
211
211
|
skipSourceValidation?: boolean;
|
|
212
212
|
}): Promise<LocalPlanBridgeServer>;
|
|
213
213
|
/**
|
|
214
|
-
* Ask
|
|
214
|
+
* Ask a loopback Plan app to validate the folder against its real renderer schema
|
|
215
215
|
* (`parsePlanMdxFolder` + `planContentSchema`) via the public, no-DB
|
|
216
216
|
* `validate-local-plan-source` action. This is what makes `verify`
|
|
217
|
-
* authoritative
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
217
|
+
* authoritative without transmitting local plan source off-device. Remote app
|
|
218
|
+
* URLs are intentionally skipped: local-files privacy mode must never POST MDX
|
|
219
|
+
* or assets to a hosted validation action. Degrades gracefully (`ran: false`)
|
|
220
|
+
* when no local Plan app is running or it predates the action.
|
|
221
221
|
*/
|
|
222
222
|
export declare function fetchRendererValidation(input: {
|
|
223
223
|
appUrl: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan-local.d.ts","sourceRoot":"","sources":["../../src/cli/plan-local.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"plan-local.d.ts","sourceRoot":"","sources":["../../src/cli/plan-local.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/plan-local.js
CHANGED
|
@@ -272,7 +272,16 @@ function repoRelativePlanPath(dir) {
|
|
|
272
272
|
return normalizeSlash(relative);
|
|
273
273
|
}
|
|
274
274
|
function localPlanBridgePageUrl(input) {
|
|
275
|
-
|
|
275
|
+
// Keep the real local folder name out of the hosted request path. The bridge
|
|
276
|
+
// payload supplies the actual slug after the browser connects on loopback.
|
|
277
|
+
// The opaque id keeps simultaneous bridge sessions distinct without
|
|
278
|
+
// disclosing the folder name or access token to the hosted request.
|
|
279
|
+
const bridgeId = crypto
|
|
280
|
+
.createHash("sha256")
|
|
281
|
+
.update(input.bridgeUrl)
|
|
282
|
+
.digest("hex")
|
|
283
|
+
.slice(0, 16);
|
|
284
|
+
return `${normalizeBridgeAppUrl(input.appUrl)}/local-plans/local-${bridgeId}#bridge=${encodeURIComponent(input.bridgeUrl)}`;
|
|
276
285
|
}
|
|
277
286
|
function writeLocalPlanUrlFile(dir, url, urlFile) {
|
|
278
287
|
const file = path.resolve(urlFile || path.join(dir, ".plan-url"));
|
|
@@ -1711,18 +1720,38 @@ function localPlanBridgeWarnings(input) {
|
|
|
1711
1720
|
return warnings;
|
|
1712
1721
|
}
|
|
1713
1722
|
const VALIDATE_LOCAL_PLAN_ACTION = "validate-local-plan-source";
|
|
1723
|
+
function isLoopbackAppUrl(value) {
|
|
1724
|
+
try {
|
|
1725
|
+
const hostname = new URL(value).hostname.toLowerCase();
|
|
1726
|
+
return (hostname === "localhost" ||
|
|
1727
|
+
hostname === "[::1]" ||
|
|
1728
|
+
hostname === "::1" ||
|
|
1729
|
+
hostname === "0.0.0.0" ||
|
|
1730
|
+
hostname.startsWith("127."));
|
|
1731
|
+
}
|
|
1732
|
+
catch {
|
|
1733
|
+
return false;
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1714
1736
|
/**
|
|
1715
|
-
* Ask
|
|
1737
|
+
* Ask a loopback Plan app to validate the folder against its real renderer schema
|
|
1716
1738
|
* (`parsePlanMdxFolder` + `planContentSchema`) via the public, no-DB
|
|
1717
1739
|
* `validate-local-plan-source` action. This is what makes `verify`
|
|
1718
|
-
* authoritative
|
|
1719
|
-
*
|
|
1720
|
-
*
|
|
1721
|
-
*
|
|
1740
|
+
* authoritative without transmitting local plan source off-device. Remote app
|
|
1741
|
+
* URLs are intentionally skipped: local-files privacy mode must never POST MDX
|
|
1742
|
+
* or assets to a hosted validation action. Degrades gracefully (`ran: false`)
|
|
1743
|
+
* when no local Plan app is running or it predates the action.
|
|
1722
1744
|
*/
|
|
1723
1745
|
export async function fetchRendererValidation(input) {
|
|
1724
1746
|
const fetchFn = input.fetchFn ?? fetch;
|
|
1725
1747
|
const endpoint = planActionEndpoint(input.appUrl, VALIDATE_LOCAL_PLAN_ACTION);
|
|
1748
|
+
if (!isLoopbackAppUrl(input.appUrl)) {
|
|
1749
|
+
return {
|
|
1750
|
+
ran: false,
|
|
1751
|
+
endpoint,
|
|
1752
|
+
error: "Skipped remote renderer validation to keep local plan source on this device.",
|
|
1753
|
+
};
|
|
1754
|
+
}
|
|
1726
1755
|
try {
|
|
1727
1756
|
const response = await fetchActionWithTimeout(endpoint, {
|
|
1728
1757
|
method: "POST",
|
|
@@ -1767,7 +1796,7 @@ export async function fetchRendererValidation(input) {
|
|
|
1767
1796
|
}
|
|
1768
1797
|
function rendererValidationWarnings(validation, offlineIssues) {
|
|
1769
1798
|
if (!validation.ran) {
|
|
1770
|
-
const base = `Plan content was NOT validated against
|
|
1799
|
+
const base = `Plan content was NOT validated against a local renderer schema (${validation.error ?? "validate endpoint unavailable"}). Fell back to the offline lint. For authoritative validation without data egress, run a local Plan app that exposes ${VALIDATE_LOCAL_PLAN_ACTION} and pass --app-url http://localhost:8096.`;
|
|
1771
1800
|
if (offlineIssues.length === 0)
|
|
1772
1801
|
return [base];
|
|
1773
1802
|
const preview = offlineIssues
|