@agent-native/core 0.72.3 → 0.73.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.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +38 -0
- package/corpus/core/docs/content/skills-guide.md +14 -0
- package/corpus/core/docs/content/template-clips.md +7 -1
- package/corpus/core/package.json +6 -6
- package/corpus/core/src/a2a/client.ts +7 -3
- package/corpus/core/src/agent/durable-background.ts +25 -12
- package/corpus/core/src/cli/create.ts +1 -1
- package/corpus/core/src/cli/skills.ts +450 -19
- package/corpus/core/src/client/api-path.ts +28 -0
- package/corpus/core/src/client/embed-auth.ts +26 -0
- package/corpus/core/src/client/mcp-app-host.ts +6 -0
- package/corpus/core/src/client/mcp-apps/McpAppRenderer.tsx +227 -33
- package/corpus/core/src/client/resources/use-mcp-servers.ts +1 -0
- package/corpus/core/src/client/settings/DemoModeSection.tsx +3 -1
- package/corpus/core/src/demo/fetch-interceptor.ts +1 -1
- package/corpus/core/src/embedding/agent.ts +6 -1
- package/corpus/core/src/embedding/bridge.ts +34 -1
- package/corpus/core/src/mcp/build-server.ts +38 -10
- package/corpus/core/src/mcp/embed-app.ts +120 -45
- package/corpus/core/src/mcp/index.ts +5 -0
- package/corpus/core/src/mcp/org-directory.ts +70 -11
- package/corpus/core/src/mcp/server.ts +14 -0
- package/corpus/core/src/mcp-client/config.ts +12 -0
- package/corpus/core/src/mcp-client/index.ts +2 -0
- package/corpus/core/src/mcp-client/manager.ts +268 -9
- package/corpus/core/src/mcp-client/remote-store.ts +154 -0
- package/corpus/core/src/mcp-client/routes.ts +2 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +8 -0
- package/corpus/core/src/server/auth.ts +7 -0
- package/corpus/core/src/server/index.ts +1 -0
- package/corpus/core/src/server/onboarding-html.ts +95 -9
- package/corpus/core/src/shared/mcp-embed-headers.ts +44 -3
- package/corpus/core/src/templates/default/AGENTS.md +4 -0
- package/corpus/core/src/templates/default/package.json +2 -1
- package/corpus/core/src/templates/headless/AGENTS.md +4 -0
- package/corpus/core/src/templates/headless/package.json +1 -0
- package/corpus/core/src/templates/workspace-root/AGENTS.md +4 -0
- package/corpus/core/src/templates/workspace-root/README.md +4 -0
- package/corpus/core/src/templates/workspace-root/package.json +1 -0
- package/corpus/core/src/vite/client.ts +53 -0
- package/corpus/templates/analytics/actions/update-dashboard.ts +3 -4
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +144 -40
- package/corpus/templates/analytics/app/global.css +15 -13
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +9 -32
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +0 -26
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +0 -23
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/pivot.ts +65 -2
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +3 -4
- package/corpus/templates/analytics/changelog/2026-06-24-dashboard-charts-now-fill-sparse-template-time-series-suppor.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-24-dashboard-rows-now-auto-fit-panels-so-one-two-or-three-chart.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-24-dashboard-traffic-tables-now-show-top-urls-and-shared-clips-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-24-first-party-signup-and-pageview-panels-now-honor-dashboard-filters.md +5 -0
- package/corpus/templates/analytics/changelog/2026-06-24-first-party-template-dashboards-now-include-repeat-users-ret.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-24-retention-charts-now-break-out-1-day-and-7-day-return-rates-.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +256 -44
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +7 -2
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +35 -8
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +287 -42
- package/corpus/templates/assets/.agents/skills/a2a-assets/SKILL.md +17 -10
- package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +15 -6
- package/corpus/templates/assets/AGENTS.md +7 -5
- package/corpus/templates/assets/actions/generate-asset.ts +161 -0
- package/corpus/templates/assets/actions/list-assets.ts +4 -2
- package/corpus/templates/assets/actions/open-asset-picker.ts +84 -3
- package/corpus/templates/assets/app/routes/library.tsx +243 -40
- package/corpus/templates/calendar/changelog/2026-06-23-added-a-heads-up-explaining-google-s-app-not-verified-screen.md +1 -1
- package/corpus/templates/calendar/server/plugins/auth.ts +12 -0
- package/corpus/templates/clips/.agents/skills/recording/SKILL.md +7 -7
- package/corpus/templates/clips/AGENTS.md +6 -2
- package/corpus/templates/clips/app/components/capture-install-options.tsx +20 -4
- package/corpus/templates/clips/app/components/player/player-controls.tsx +62 -32
- package/corpus/templates/clips/app/lib/capture-install-options.ts +39 -5
- package/corpus/templates/clips/app/routes/download.tsx +2 -2
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +1 -1
- package/corpus/templates/clips/changelog/2026-06-24-inline-slack-playback-controls-no-longer-shift-when-adjustin.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-24-shared-clips-now-expose-their-full-console-log-stream-all-le.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-24-shared-clips-now-expose-their-full-fetch-xhr-network-request.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-24-the-chrome-extension-option-now-only-appears-on-supported-hosts.md +5 -0
- package/corpus/templates/clips/chrome-extension/STORE_DRAFT.md +13 -8
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
- package/corpus/templates/clips/chrome-extension/scripts/package.ts +15 -2
- package/corpus/templates/clips/chrome-extension/src/overlay.css +4 -25
- package/corpus/templates/clips/chrome-extension/src/overlay.ts +1 -3
- package/corpus/templates/clips/desktop/src-tauri/src/native_speech.rs +9 -0
- package/corpus/templates/clips/server/lib/public-agent-context.ts +67 -24
- package/corpus/templates/design/AGENTS.md +10 -5
- package/corpus/templates/design/actions/connect-assets-mcp.ts +131 -0
- package/corpus/templates/design/actions/insert-asset.ts +219 -0
- package/corpus/templates/design/actions/view-screen.ts +3 -1
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +1 -1
- package/corpus/templates/design/app/pages/DesignEditor.tsx +53 -0
- package/corpus/templates/design/server/plugins/agent-chat.ts +5 -1
- package/corpus/templates/design/server/plugins/assets-mcp.ts +60 -0
- package/corpus/templates/mail/server/plugins/auth.ts +4 -1
- package/corpus/templates/plan/app/pages/PlansPage.tsx +271 -197
- package/corpus/templates/plan/server/plan-content.ts +45 -2
- package/dist/a2a/client.d.ts +1 -0
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +5 -3
- package/dist/a2a/client.js.map +1 -1
- package/dist/agent/durable-background.d.ts +9 -4
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +24 -11
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/cli/create.js +1 -1
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +361 -19
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/api-path.d.ts.map +1 -1
- package/dist/client/api-path.js +27 -0
- package/dist/client/api-path.js.map +1 -1
- package/dist/client/embed-auth.d.ts.map +1 -1
- package/dist/client/embed-auth.js +27 -0
- package/dist/client/embed-auth.js.map +1 -1
- package/dist/client/mcp-app-host.d.ts.map +1 -1
- package/dist/client/mcp-app-host.js +5 -0
- package/dist/client/mcp-app-host.js.map +1 -1
- package/dist/client/mcp-apps/McpAppRenderer.d.ts +1 -0
- package/dist/client/mcp-apps/McpAppRenderer.d.ts.map +1 -1
- package/dist/client/mcp-apps/McpAppRenderer.js +179 -33
- package/dist/client/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/client/resources/use-mcp-servers.d.ts +1 -0
- package/dist/client/resources/use-mcp-servers.d.ts.map +1 -1
- package/dist/client/resources/use-mcp-servers.js.map +1 -1
- package/dist/client/settings/DemoModeSection.d.ts.map +1 -1
- package/dist/client/settings/DemoModeSection.js +3 -1
- package/dist/client/settings/DemoModeSection.js.map +1 -1
- package/dist/demo/fetch-interceptor.js +1 -1
- package/dist/demo/fetch-interceptor.js.map +1 -1
- package/dist/embedding/agent.d.ts +1 -1
- package/dist/embedding/agent.d.ts.map +1 -1
- package/dist/embedding/agent.js +1 -1
- package/dist/embedding/agent.js.map +1 -1
- package/dist/embedding/bridge.d.ts.map +1 -1
- package/dist/embedding/bridge.js +27 -1
- package/dist/embedding/bridge.js.map +1 -1
- package/dist/extensions/schema.d.ts +2 -2
- package/dist/mcp/build-server.d.ts +2 -0
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +32 -9
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/embed-app.d.ts.map +1 -1
- package/dist/mcp/embed-app.js +120 -45
- package/dist/mcp/embed-app.js.map +1 -1
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +1 -0
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/org-directory.d.ts +1 -0
- package/dist/mcp/org-directory.d.ts.map +1 -1
- package/dist/mcp/org-directory.js +59 -11
- package/dist/mcp/org-directory.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +9 -0
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp-client/config.d.ts +12 -0
- package/dist/mcp-client/config.d.ts.map +1 -1
- package/dist/mcp-client/config.js.map +1 -1
- package/dist/mcp-client/index.d.ts +1 -1
- package/dist/mcp-client/index.d.ts.map +1 -1
- package/dist/mcp-client/index.js +1 -1
- package/dist/mcp-client/index.js.map +1 -1
- package/dist/mcp-client/manager.d.ts +2 -0
- package/dist/mcp-client/manager.d.ts.map +1 -1
- package/dist/mcp-client/manager.js +201 -9
- package/dist/mcp-client/manager.js.map +1 -1
- package/dist/mcp-client/remote-store.d.ts +27 -0
- package/dist/mcp-client/remote-store.d.ts.map +1 -1
- package/dist/mcp-client/remote-store.js +105 -0
- package/dist/mcp-client/remote-store.js.map +1 -1
- package/dist/mcp-client/routes.d.ts +1 -0
- package/dist/mcp-client/routes.d.ts.map +1 -1
- package/dist/mcp-client/routes.js +1 -0
- package/dist/mcp-client/routes.js.map +1 -1
- package/dist/provider-api/actions/github-repo-files.d.ts +2 -2
- package/dist/provider-api/actions/query-staged-dataset.d.ts +3 -3
- package/dist/provider-api/corpus-jobs.d.ts +8 -8
- package/dist/server/agent-chat-plugin.d.ts +2 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +8 -0
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/auth.d.ts +7 -0
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +1 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/onboarding-html.d.ts +15 -0
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +73 -9
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/shared/mcp-embed-headers.d.ts +3 -1
- package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
- package/dist/shared/mcp-embed-headers.js +40 -3
- package/dist/shared/mcp-embed-headers.js.map +1 -1
- package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
- package/dist/sharing/actions/set-resource-visibility.d.ts +2 -2
- package/dist/sharing/actions/share-resource.d.ts +1 -1
- package/dist/sharing/schema.d.ts +1 -1
- package/dist/templates/default/AGENTS.md +4 -0
- package/dist/templates/default/package.json +2 -1
- package/dist/templates/headless/AGENTS.md +4 -0
- package/dist/templates/headless/package.json +1 -0
- package/dist/templates/workspace-root/AGENTS.md +4 -0
- package/dist/templates/workspace-root/README.md +4 -0
- package/dist/templates/workspace-root/package.json +1 -0
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +44 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/skills-guide.md +14 -0
- package/docs/content/template-clips.md +7 -1
- package/package.json +6 -6
- package/src/templates/default/AGENTS.md +4 -0
- package/src/templates/default/package.json +2 -1
- package/src/templates/headless/AGENTS.md +4 -0
- package/src/templates/headless/package.json +1 -0
- package/src/templates/workspace-root/AGENTS.md +4 -0
- package/src/templates/workspace-root/README.md +4 -0
- package/src/templates/workspace-root/package.json +1 -0
|
@@ -31,13 +31,14 @@ import {
|
|
|
31
31
|
} from "./context-xray-local.js";
|
|
32
32
|
import { CLIENTS, type ClientId } from "./mcp-config-writers.js";
|
|
33
33
|
import { PR_VISUAL_RECAP_SETUP, writePrVisualRecapWorkflow } from "./recap.js";
|
|
34
|
+
import { setupAgentSymlinks } from "./setup-agents.js";
|
|
34
35
|
|
|
35
36
|
const HELP = `npx @agent-native/core@latest skills
|
|
36
37
|
|
|
37
38
|
Usage:
|
|
38
39
|
npx @agent-native/core@latest skills list
|
|
39
|
-
npx @agent-native/core@latest skills status [assets|content|design-exploration|visual-plan|visual-recap|context-xray] [--client codex|claude-code|pi|all] [--scope user|project] [--json]
|
|
40
|
-
npx @agent-native/core@latest skills update [assets|content|design-exploration|visual-plan|visual-recap|context-xray] [--client codex|claude-code|pi|all] [--scope user|project] [--dry-run] [--json]
|
|
40
|
+
npx @agent-native/core@latest skills status [assets|content|design-exploration|visual-plan|visual-recap|context-xray|scaffold] [--client codex|claude-code|pi|all] [--scope user|project] [--json]
|
|
41
|
+
npx @agent-native/core@latest skills update [assets|content|design-exploration|visual-plan|visual-recap|context-xray|scaffold] [--client codex|claude-code|pi|all] [--scope user|project] [--dry-run] [--json]
|
|
41
42
|
npx @agent-native/core@latest skills add assets|content|design-exploration|visual-plan|visual-recap|context-xray [--client codex|claude-code|cowork|cursor|opencode|github-copilot|all] [--scope user|project] [--mode hosted|local-files|self-hosted] [--mcp-url <url>] [--no-connect] [--with-github-action] [--yes] [--dry-run] [--json]
|
|
42
43
|
npx @agent-native/core@latest skills add <manifest-or-app-dir|skill-repo> [--skill <name>] [--client ...] [--yes]
|
|
43
44
|
|
|
@@ -52,6 +53,7 @@ Examples:
|
|
|
52
53
|
npx @agent-native/core@latest skills add visual-plan --mode self-hosted --mcp-url https://my-plan-app.example.com
|
|
53
54
|
npx @agent-native/core@latest skills status visual-plan
|
|
54
55
|
npx @agent-native/core@latest skills update visual-plan
|
|
56
|
+
npx @agent-native/core@latest skills update scaffold --project
|
|
55
57
|
npx @agent-native/core@latest skills add visual-plan --no-connect
|
|
56
58
|
npx @agent-native/core@latest skills add context-xray --client all
|
|
57
59
|
npx @agent-native/core@latest skills add assets --client claude-code
|
|
@@ -101,7 +103,10 @@ run "npx @agent-native/core@latest recap setup" / "npx @agent-native/core@latest
|
|
|
101
103
|
verify GitHub Actions. Docs: https://www.agent-native.com/docs/pr-visual-recap.
|
|
102
104
|
|
|
103
105
|
The status/update commands inspect copied Agent Native skill folders and refresh
|
|
104
|
-
their instruction files from the current @agent-native/core package
|
|
106
|
+
their instruction files from the current @agent-native/core package. In generated
|
|
107
|
+
apps/workspaces, "skills update scaffold --project" refreshes the framework
|
|
108
|
+
skills copied into the scaffold and repairs AGENTS.md / CLAUDE.md and
|
|
109
|
+
.agents/skills / .claude/skills compatibility links.`;
|
|
105
110
|
|
|
106
111
|
const ASSETS_SKILL_MD = `---
|
|
107
112
|
name: assets
|
|
@@ -2844,6 +2849,19 @@ interface SkillInstallState {
|
|
|
2844
2849
|
managed: boolean;
|
|
2845
2850
|
}
|
|
2846
2851
|
|
|
2852
|
+
interface ScaffoldGuidanceState {
|
|
2853
|
+
kind: "workspace-core" | "standalone";
|
|
2854
|
+
displayName: string;
|
|
2855
|
+
templateName: "workspace-core" | "headless" | "default";
|
|
2856
|
+
path: string;
|
|
2857
|
+
sourcePath: string;
|
|
2858
|
+
projectRoot: string;
|
|
2859
|
+
workspaceRoot?: string;
|
|
2860
|
+
sharedPackageDir?: string;
|
|
2861
|
+
current: boolean;
|
|
2862
|
+
skillCount: number;
|
|
2863
|
+
}
|
|
2864
|
+
|
|
2847
2865
|
interface SkillInstallTarget {
|
|
2848
2866
|
id: string;
|
|
2849
2867
|
displayName: string;
|
|
@@ -3591,7 +3609,368 @@ function skillSearchRoots(input: {
|
|
|
3591
3609
|
});
|
|
3592
3610
|
}
|
|
3593
3611
|
|
|
3612
|
+
const SCAFFOLD_GUIDANCE_TARGETS = new Set([
|
|
3613
|
+
"scaffold",
|
|
3614
|
+
"generated",
|
|
3615
|
+
"generated-app",
|
|
3616
|
+
"generated-workspace",
|
|
3617
|
+
"workspace",
|
|
3618
|
+
"workspace-core",
|
|
3619
|
+
"framework-guidance",
|
|
3620
|
+
]);
|
|
3621
|
+
|
|
3622
|
+
function isScaffoldGuidanceTarget(value: string | undefined): boolean {
|
|
3623
|
+
if (!value) return false;
|
|
3624
|
+
return SCAFFOLD_GUIDANCE_TARGETS.has(value.trim().toLowerCase());
|
|
3625
|
+
}
|
|
3626
|
+
|
|
3627
|
+
function corePackageRootDir(): string {
|
|
3628
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
3629
|
+
return path.resolve(here, "../..");
|
|
3630
|
+
}
|
|
3631
|
+
|
|
3632
|
+
function bundledScaffoldSkillsDir(
|
|
3633
|
+
templateName: ScaffoldGuidanceState["templateName"],
|
|
3634
|
+
): string {
|
|
3635
|
+
return path.join(
|
|
3636
|
+
corePackageRootDir(),
|
|
3637
|
+
"src",
|
|
3638
|
+
"templates",
|
|
3639
|
+
templateName,
|
|
3640
|
+
".agents",
|
|
3641
|
+
"skills",
|
|
3642
|
+
);
|
|
3643
|
+
}
|
|
3644
|
+
|
|
3645
|
+
function readJsonRecord(file: string): Record<string, unknown> | undefined {
|
|
3646
|
+
try {
|
|
3647
|
+
const parsed = JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
3648
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
3649
|
+
return parsed as Record<string, unknown>;
|
|
3650
|
+
}
|
|
3651
|
+
} catch {}
|
|
3652
|
+
return undefined;
|
|
3653
|
+
}
|
|
3654
|
+
|
|
3655
|
+
function readPackageJson(dir: string): Record<string, unknown> | undefined {
|
|
3656
|
+
return readJsonRecord(path.join(dir, "package.json"));
|
|
3657
|
+
}
|
|
3658
|
+
|
|
3659
|
+
function packageName(pkg: Record<string, unknown> | undefined): string | null {
|
|
3660
|
+
return typeof pkg?.name === "string" ? pkg.name : null;
|
|
3661
|
+
}
|
|
3662
|
+
|
|
3663
|
+
function workspaceCorePackageName(
|
|
3664
|
+
pkg: Record<string, unknown> | undefined,
|
|
3665
|
+
): string | null {
|
|
3666
|
+
const agentNative = pkg?.["agent-native"];
|
|
3667
|
+
if (
|
|
3668
|
+
agentNative &&
|
|
3669
|
+
typeof agentNative === "object" &&
|
|
3670
|
+
!Array.isArray(agentNative) &&
|
|
3671
|
+
typeof (agentNative as Record<string, unknown>).workspaceCore === "string"
|
|
3672
|
+
) {
|
|
3673
|
+
return (agentNative as Record<string, string>).workspaceCore;
|
|
3674
|
+
}
|
|
3675
|
+
return null;
|
|
3676
|
+
}
|
|
3677
|
+
|
|
3678
|
+
function hasAgentNativeCoreDependency(
|
|
3679
|
+
pkg: Record<string, unknown> | undefined,
|
|
3680
|
+
): boolean {
|
|
3681
|
+
for (const field of ["dependencies", "devDependencies", "peerDependencies"]) {
|
|
3682
|
+
const deps = pkg?.[field];
|
|
3683
|
+
if (
|
|
3684
|
+
deps &&
|
|
3685
|
+
typeof deps === "object" &&
|
|
3686
|
+
!Array.isArray(deps) &&
|
|
3687
|
+
"@agent-native/core" in deps
|
|
3688
|
+
) {
|
|
3689
|
+
return true;
|
|
3690
|
+
}
|
|
3691
|
+
}
|
|
3692
|
+
return false;
|
|
3693
|
+
}
|
|
3694
|
+
|
|
3695
|
+
function findWorkspaceCorePackageDir(
|
|
3696
|
+
workspaceRoot: string,
|
|
3697
|
+
workspaceCoreName: string,
|
|
3698
|
+
): string | undefined {
|
|
3699
|
+
const packagesDir = path.join(workspaceRoot, "packages");
|
|
3700
|
+
if (!fs.existsSync(packagesDir)) return undefined;
|
|
3701
|
+
for (const entry of fs.readdirSync(packagesDir, { withFileTypes: true })) {
|
|
3702
|
+
if (!entry.isDirectory()) continue;
|
|
3703
|
+
const candidate = path.join(packagesDir, entry.name);
|
|
3704
|
+
if (packageName(readPackageJson(candidate)) === workspaceCoreName) {
|
|
3705
|
+
return candidate;
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
const fallback = path.join(workspaceRoot, "packages", "shared");
|
|
3709
|
+
return fs.existsSync(path.join(fallback, "package.json"))
|
|
3710
|
+
? fallback
|
|
3711
|
+
: undefined;
|
|
3712
|
+
}
|
|
3713
|
+
|
|
3714
|
+
function findGeneratedWorkspace(startDir: string):
|
|
3715
|
+
| {
|
|
3716
|
+
workspaceRoot: string;
|
|
3717
|
+
sharedPackageDir: string;
|
|
3718
|
+
}
|
|
3719
|
+
| undefined {
|
|
3720
|
+
let current = path.resolve(startDir);
|
|
3721
|
+
while (true) {
|
|
3722
|
+
const pkg = readPackageJson(current);
|
|
3723
|
+
const workspaceCoreName = workspaceCorePackageName(pkg);
|
|
3724
|
+
if (workspaceCoreName && fs.existsSync(path.join(current, "apps"))) {
|
|
3725
|
+
const sharedPackageDir = findWorkspaceCorePackageDir(
|
|
3726
|
+
current,
|
|
3727
|
+
workspaceCoreName,
|
|
3728
|
+
);
|
|
3729
|
+
if (sharedPackageDir) {
|
|
3730
|
+
return { workspaceRoot: current, sharedPackageDir };
|
|
3731
|
+
}
|
|
3732
|
+
}
|
|
3733
|
+
const parent = path.dirname(current);
|
|
3734
|
+
if (parent === current) break;
|
|
3735
|
+
current = parent;
|
|
3736
|
+
}
|
|
3737
|
+
return undefined;
|
|
3738
|
+
}
|
|
3739
|
+
|
|
3740
|
+
function detectStandaloneScaffoldTemplate(
|
|
3741
|
+
projectRoot: string,
|
|
3742
|
+
): "headless" | "default" | undefined {
|
|
3743
|
+
const pkg = readPackageJson(projectRoot);
|
|
3744
|
+
if (!hasAgentNativeCoreDependency(pkg)) return undefined;
|
|
3745
|
+
if (!fs.existsSync(path.join(projectRoot, ".agents", "skills"))) {
|
|
3746
|
+
return undefined;
|
|
3747
|
+
}
|
|
3748
|
+
|
|
3749
|
+
const hasAppDir = fs.existsSync(path.join(projectRoot, "app"));
|
|
3750
|
+
const hasHeadlessHello = fs.existsSync(
|
|
3751
|
+
path.join(projectRoot, "actions", "hello.ts"),
|
|
3752
|
+
);
|
|
3753
|
+
if (!hasAppDir && hasHeadlessHello) return "headless";
|
|
3754
|
+
|
|
3755
|
+
const looksLikeDefaultTemplate =
|
|
3756
|
+
fs.existsSync(path.join(projectRoot, "app", "routes", "database.tsx")) &&
|
|
3757
|
+
fs.existsSync(path.join(projectRoot, "app", "routes", "_index.tsx")) &&
|
|
3758
|
+
fs.existsSync(path.join(projectRoot, "actions", "view-screen.ts"));
|
|
3759
|
+
return looksLikeDefaultTemplate ? "default" : undefined;
|
|
3760
|
+
}
|
|
3761
|
+
|
|
3762
|
+
function listImmediateSkillDirs(dir: string): string[] {
|
|
3763
|
+
if (!fs.existsSync(dir)) return [];
|
|
3764
|
+
return fs
|
|
3765
|
+
.readdirSync(dir, { withFileTypes: true })
|
|
3766
|
+
.filter((entry) => entry.isDirectory())
|
|
3767
|
+
.map((entry) => entry.name)
|
|
3768
|
+
.sort();
|
|
3769
|
+
}
|
|
3770
|
+
|
|
3771
|
+
function skillDirContentsMatch(sourceDir: string, targetDir: string): boolean {
|
|
3772
|
+
const expected = listSkillFolderFiles(sourceDir);
|
|
3773
|
+
const actual = listSkillFolderFiles(targetDir);
|
|
3774
|
+
const expectedFiles = Object.keys(expected).sort();
|
|
3775
|
+
const actualFiles = Object.keys(actual).sort();
|
|
3776
|
+
if (expectedFiles.length !== actualFiles.length) return false;
|
|
3777
|
+
for (let i = 0; i < expectedFiles.length; i += 1) {
|
|
3778
|
+
if (expectedFiles[i] !== actualFiles[i]) return false;
|
|
3779
|
+
}
|
|
3780
|
+
return expectedFiles.every((file) => expected[file] === actual[file]);
|
|
3781
|
+
}
|
|
3782
|
+
|
|
3783
|
+
function scaffoldGuidanceCurrent(
|
|
3784
|
+
sourceRoot: string,
|
|
3785
|
+
targetRoot: string,
|
|
3786
|
+
): boolean {
|
|
3787
|
+
const skills = listImmediateSkillDirs(sourceRoot);
|
|
3788
|
+
if (skills.length === 0) return false;
|
|
3789
|
+
return skills.every((skill) =>
|
|
3790
|
+
skillDirContentsMatch(
|
|
3791
|
+
path.join(sourceRoot, skill),
|
|
3792
|
+
path.join(targetRoot, skill),
|
|
3793
|
+
),
|
|
3794
|
+
);
|
|
3795
|
+
}
|
|
3796
|
+
|
|
3797
|
+
function collectScaffoldGuidanceStates(
|
|
3798
|
+
parsed: ParsedSkillsArgs,
|
|
3799
|
+
options: RunSkillsOptions,
|
|
3800
|
+
): ScaffoldGuidanceState[] {
|
|
3801
|
+
if (parsed.target && !isScaffoldGuidanceTarget(parsed.target)) return [];
|
|
3802
|
+
if (parsed.scopeExplicit && parsed.scope !== "project") return [];
|
|
3803
|
+
|
|
3804
|
+
const baseDir = path.resolve(options.baseDir ?? process.cwd());
|
|
3805
|
+
const workspace = findGeneratedWorkspace(baseDir);
|
|
3806
|
+
if (workspace) {
|
|
3807
|
+
const sourcePath = bundledScaffoldSkillsDir("workspace-core");
|
|
3808
|
+
const targetPath = path.join(
|
|
3809
|
+
workspace.sharedPackageDir,
|
|
3810
|
+
".agents",
|
|
3811
|
+
"skills",
|
|
3812
|
+
);
|
|
3813
|
+
if (!fs.existsSync(sourcePath)) return [];
|
|
3814
|
+
return [
|
|
3815
|
+
{
|
|
3816
|
+
kind: "workspace-core",
|
|
3817
|
+
displayName: "Generated workspace framework skills",
|
|
3818
|
+
templateName: "workspace-core",
|
|
3819
|
+
path: targetPath,
|
|
3820
|
+
sourcePath,
|
|
3821
|
+
projectRoot: workspace.workspaceRoot,
|
|
3822
|
+
workspaceRoot: workspace.workspaceRoot,
|
|
3823
|
+
sharedPackageDir: workspace.sharedPackageDir,
|
|
3824
|
+
current: scaffoldGuidanceCurrent(sourcePath, targetPath),
|
|
3825
|
+
skillCount: listImmediateSkillDirs(sourcePath).length,
|
|
3826
|
+
},
|
|
3827
|
+
];
|
|
3828
|
+
}
|
|
3829
|
+
|
|
3830
|
+
const templateName = detectStandaloneScaffoldTemplate(baseDir);
|
|
3831
|
+
if (!templateName) return [];
|
|
3832
|
+
const sourcePath = bundledScaffoldSkillsDir(templateName);
|
|
3833
|
+
const targetPath = path.join(baseDir, ".agents", "skills");
|
|
3834
|
+
if (!fs.existsSync(sourcePath)) return [];
|
|
3835
|
+
return [
|
|
3836
|
+
{
|
|
3837
|
+
kind: "standalone",
|
|
3838
|
+
displayName: `Generated ${templateName} app framework skills`,
|
|
3839
|
+
templateName,
|
|
3840
|
+
path: targetPath,
|
|
3841
|
+
sourcePath,
|
|
3842
|
+
projectRoot: baseDir,
|
|
3843
|
+
current: scaffoldGuidanceCurrent(sourcePath, targetPath),
|
|
3844
|
+
skillCount: listImmediateSkillDirs(sourcePath).length,
|
|
3845
|
+
},
|
|
3846
|
+
];
|
|
3847
|
+
}
|
|
3848
|
+
|
|
3849
|
+
function copyScaffoldGuidanceSkills(
|
|
3850
|
+
sourceRoot: string,
|
|
3851
|
+
targetRoot: string,
|
|
3852
|
+
): void {
|
|
3853
|
+
fs.mkdirSync(targetRoot, { recursive: true });
|
|
3854
|
+
for (const skill of listImmediateSkillDirs(sourceRoot)) {
|
|
3855
|
+
const targetSkillDir = path.join(targetRoot, skill);
|
|
3856
|
+
if (
|
|
3857
|
+
fs.existsSync(targetSkillDir) &&
|
|
3858
|
+
fs.lstatSync(targetSkillDir).isSymbolicLink()
|
|
3859
|
+
) {
|
|
3860
|
+
continue;
|
|
3861
|
+
}
|
|
3862
|
+
fs.rmSync(targetSkillDir, { recursive: true, force: true });
|
|
3863
|
+
fs.cpSync(path.join(sourceRoot, skill), targetSkillDir, {
|
|
3864
|
+
recursive: true,
|
|
3865
|
+
});
|
|
3866
|
+
}
|
|
3867
|
+
}
|
|
3868
|
+
|
|
3869
|
+
function updateScaffoldGuidanceStates(
|
|
3870
|
+
states: ScaffoldGuidanceState[],
|
|
3871
|
+
dryRun: boolean,
|
|
3872
|
+
): ScaffoldGuidanceState[] {
|
|
3873
|
+
const updated: ScaffoldGuidanceState[] = [];
|
|
3874
|
+
for (const state of states) {
|
|
3875
|
+
if (state.current) continue;
|
|
3876
|
+
if (!dryRun) {
|
|
3877
|
+
copyScaffoldGuidanceSkills(state.sourcePath, state.path);
|
|
3878
|
+
}
|
|
3879
|
+
updated.push({
|
|
3880
|
+
...state,
|
|
3881
|
+
current: !dryRun,
|
|
3882
|
+
});
|
|
3883
|
+
}
|
|
3884
|
+
return updated;
|
|
3885
|
+
}
|
|
3886
|
+
|
|
3887
|
+
function ensureWorkspaceRootSkillsLink(
|
|
3888
|
+
workspaceRoot: string,
|
|
3889
|
+
sharedPackageDir: string,
|
|
3890
|
+
): void {
|
|
3891
|
+
const sharedSkillsDir = path.join(sharedPackageDir, ".agents", "skills");
|
|
3892
|
+
if (!fs.existsSync(sharedSkillsDir)) return;
|
|
3893
|
+
|
|
3894
|
+
const agentsDir = path.join(workspaceRoot, ".agents");
|
|
3895
|
+
const linkPath = path.join(agentsDir, "skills");
|
|
3896
|
+
const target = path.relative(agentsDir, sharedSkillsDir);
|
|
3897
|
+
|
|
3898
|
+
fs.mkdirSync(agentsDir, { recursive: true });
|
|
3899
|
+
try {
|
|
3900
|
+
const stat = fs.lstatSync(linkPath);
|
|
3901
|
+
if (stat.isSymbolicLink()) {
|
|
3902
|
+
if (fs.readlinkSync(linkPath) === target) return;
|
|
3903
|
+
fs.unlinkSync(linkPath);
|
|
3904
|
+
} else {
|
|
3905
|
+
copyScaffoldGuidanceSkills(sharedSkillsDir, linkPath);
|
|
3906
|
+
return;
|
|
3907
|
+
}
|
|
3908
|
+
} catch {}
|
|
3909
|
+
|
|
3910
|
+
try {
|
|
3911
|
+
fs.symlinkSync(
|
|
3912
|
+
target,
|
|
3913
|
+
linkPath,
|
|
3914
|
+
process.platform === "win32" ? "junction" : "dir",
|
|
3915
|
+
);
|
|
3916
|
+
} catch {
|
|
3917
|
+
try {
|
|
3918
|
+
fs.cpSync(sharedSkillsDir, linkPath, { recursive: true });
|
|
3919
|
+
} catch {}
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
3922
|
+
|
|
3923
|
+
function refreshCopiedClaudeSkills(projectRoot: string): void {
|
|
3924
|
+
const agentsSkillsDir = path.join(projectRoot, ".agents", "skills");
|
|
3925
|
+
const claudeSkillsDir = path.join(projectRoot, ".claude", "skills");
|
|
3926
|
+
if (!fs.existsSync(agentsSkillsDir) || !fs.existsSync(claudeSkillsDir)) {
|
|
3927
|
+
return;
|
|
3928
|
+
}
|
|
3929
|
+
try {
|
|
3930
|
+
if (fs.lstatSync(claudeSkillsDir).isSymbolicLink()) return;
|
|
3931
|
+
copyScaffoldGuidanceSkills(agentsSkillsDir, claudeSkillsDir);
|
|
3932
|
+
} catch {}
|
|
3933
|
+
}
|
|
3934
|
+
|
|
3935
|
+
function repairScaffoldAgentLinks(states: ScaffoldGuidanceState[]): void {
|
|
3936
|
+
const seen = new Set<string>();
|
|
3937
|
+
for (const state of states) {
|
|
3938
|
+
if (state.workspaceRoot && state.sharedPackageDir) {
|
|
3939
|
+
const key = `workspace:${state.workspaceRoot}`;
|
|
3940
|
+
if (seen.has(key)) continue;
|
|
3941
|
+
seen.add(key);
|
|
3942
|
+
ensureWorkspaceRootSkillsLink(
|
|
3943
|
+
state.workspaceRoot,
|
|
3944
|
+
state.sharedPackageDir,
|
|
3945
|
+
);
|
|
3946
|
+
setupAgentSymlinks(state.workspaceRoot);
|
|
3947
|
+
refreshCopiedClaudeSkills(state.workspaceRoot);
|
|
3948
|
+
setupAgentSymlinks(state.sharedPackageDir);
|
|
3949
|
+
refreshCopiedClaudeSkills(state.sharedPackageDir);
|
|
3950
|
+
const appsDir = path.join(state.workspaceRoot, "apps");
|
|
3951
|
+
if (fs.existsSync(appsDir)) {
|
|
3952
|
+
for (const entry of fs.readdirSync(appsDir, { withFileTypes: true })) {
|
|
3953
|
+
if (!entry.isDirectory()) continue;
|
|
3954
|
+
const appDir = path.join(appsDir, entry.name);
|
|
3955
|
+
if (fs.existsSync(path.join(appDir, "package.json"))) {
|
|
3956
|
+
setupAgentSymlinks(appDir);
|
|
3957
|
+
refreshCopiedClaudeSkills(appDir);
|
|
3958
|
+
}
|
|
3959
|
+
}
|
|
3960
|
+
}
|
|
3961
|
+
continue;
|
|
3962
|
+
}
|
|
3963
|
+
|
|
3964
|
+
const key = `standalone:${state.projectRoot}`;
|
|
3965
|
+
if (seen.has(key)) continue;
|
|
3966
|
+
seen.add(key);
|
|
3967
|
+
setupAgentSymlinks(state.projectRoot);
|
|
3968
|
+
refreshCopiedClaudeSkills(state.projectRoot);
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3971
|
+
|
|
3594
3972
|
function targetIdsForStatus(parsed: ParsedSkillsArgs): BuiltInAppSkillId[] {
|
|
3973
|
+
if (isScaffoldGuidanceTarget(parsed.target)) return [];
|
|
3595
3974
|
if (!parsed.target) {
|
|
3596
3975
|
return (Object.keys(BUILT_IN_APP_SKILLS) as BuiltInAppSkillId[]).filter(
|
|
3597
3976
|
(id) => !isLocalOnlyBuiltInSkill(BUILT_IN_APP_SKILLS[id]),
|
|
@@ -5423,6 +5802,27 @@ function formatSkillState(state: SkillInstallState): string {
|
|
|
5423
5802
|
return `${state.skillName.padEnd(22)} ${status.padEnd(7)} ${state.scope}/${state.client} ${managed}${hashes}\n ${state.path}`;
|
|
5424
5803
|
}
|
|
5425
5804
|
|
|
5805
|
+
function scaffoldStateJson(state: ScaffoldGuidanceState) {
|
|
5806
|
+
return {
|
|
5807
|
+
kind: state.kind,
|
|
5808
|
+
displayName: state.displayName,
|
|
5809
|
+
templateName: state.templateName,
|
|
5810
|
+
path: state.path,
|
|
5811
|
+
sourcePath: state.sourcePath,
|
|
5812
|
+
projectRoot: state.projectRoot,
|
|
5813
|
+
workspaceRoot: state.workspaceRoot,
|
|
5814
|
+
sharedPackageDir: state.sharedPackageDir,
|
|
5815
|
+
skillCount: state.skillCount,
|
|
5816
|
+
status: state.current ? "current" : "stale",
|
|
5817
|
+
managed: true,
|
|
5818
|
+
};
|
|
5819
|
+
}
|
|
5820
|
+
|
|
5821
|
+
function formatScaffoldState(state: ScaffoldGuidanceState): string {
|
|
5822
|
+
const status = state.current ? "current" : "stale";
|
|
5823
|
+
return `${"scaffold".padEnd(22)} ${status.padEnd(7)} project/${state.kind} managed (${state.skillCount} skills)\n ${state.path}`;
|
|
5824
|
+
}
|
|
5825
|
+
|
|
5426
5826
|
function planModeSummary(mode: PlanInstallMode): string {
|
|
5427
5827
|
if (mode === "local-files")
|
|
5428
5828
|
return "Local files - no hosted writes by default";
|
|
@@ -5503,25 +5903,46 @@ function runSkillsStatusOrUpdate(
|
|
|
5503
5903
|
options: RunSkillsOptions,
|
|
5504
5904
|
update: boolean,
|
|
5505
5905
|
): void {
|
|
5506
|
-
const
|
|
5507
|
-
const
|
|
5508
|
-
const
|
|
5906
|
+
const skillBefore = collectSkillInstallStates(parsed, options);
|
|
5907
|
+
const scaffoldBefore = collectScaffoldGuidanceStates(parsed, options);
|
|
5908
|
+
const skillChanged = update
|
|
5909
|
+
? updateSkillInstallStates(skillBefore, parsed.dryRun)
|
|
5910
|
+
: [];
|
|
5911
|
+
const scaffoldChanged = update
|
|
5912
|
+
? updateScaffoldGuidanceStates(scaffoldBefore, parsed.dryRun)
|
|
5913
|
+
: [];
|
|
5914
|
+
if (update && !parsed.dryRun && scaffoldChanged.length > 0) {
|
|
5915
|
+
repairScaffoldAgentLinks(scaffoldChanged);
|
|
5916
|
+
}
|
|
5917
|
+
const skillAfter =
|
|
5509
5918
|
update && !parsed.dryRun
|
|
5510
5919
|
? collectSkillInstallStates(parsed, options)
|
|
5511
|
-
:
|
|
5920
|
+
: skillBefore;
|
|
5921
|
+
const scaffoldAfter =
|
|
5922
|
+
update && !parsed.dryRun
|
|
5923
|
+
? collectScaffoldGuidanceStates(parsed, options)
|
|
5924
|
+
: scaffoldBefore;
|
|
5925
|
+
const beforeCount = skillBefore.length + scaffoldBefore.length;
|
|
5926
|
+
const changedCount = skillChanged.length + scaffoldChanged.length;
|
|
5512
5927
|
|
|
5513
5928
|
if (parsed.printJson) {
|
|
5514
|
-
const
|
|
5929
|
+
const outputSkillStates =
|
|
5930
|
+
update && !parsed.dryRun ? skillAfter : skillBefore;
|
|
5931
|
+
const outputScaffoldStates =
|
|
5932
|
+
update && !parsed.dryRun ? scaffoldAfter : scaffoldBefore;
|
|
5515
5933
|
process.stdout.write(
|
|
5516
5934
|
`${JSON.stringify(
|
|
5517
5935
|
{
|
|
5518
5936
|
ok: true,
|
|
5519
5937
|
command: parsed.command,
|
|
5520
5938
|
dryRun: parsed.dryRun,
|
|
5521
|
-
found:
|
|
5522
|
-
stale:
|
|
5523
|
-
|
|
5524
|
-
|
|
5939
|
+
found: beforeCount,
|
|
5940
|
+
stale:
|
|
5941
|
+
outputSkillStates.filter((state) => !state.current).length +
|
|
5942
|
+
outputScaffoldStates.filter((state) => !state.current).length,
|
|
5943
|
+
updated: changedCount,
|
|
5944
|
+
skills: outputSkillStates.map(skillStateJson),
|
|
5945
|
+
scaffold: outputScaffoldStates.map(scaffoldStateJson),
|
|
5525
5946
|
},
|
|
5526
5947
|
null,
|
|
5527
5948
|
2,
|
|
@@ -5530,10 +5951,13 @@ function runSkillsStatusOrUpdate(
|
|
|
5530
5951
|
return;
|
|
5531
5952
|
}
|
|
5532
5953
|
|
|
5533
|
-
if (
|
|
5954
|
+
if (beforeCount === 0) {
|
|
5534
5955
|
const target = parsed.target ? ` for ${parsed.target}` : "";
|
|
5956
|
+
const hint = isScaffoldGuidanceTarget(parsed.target)
|
|
5957
|
+
? `Run this from a generated Agent Native app or workspace root.\n`
|
|
5958
|
+
: `Run "npx @agent-native/core@latest skills add ${parsed.target ?? "visual-plan"}" to install one.\n`;
|
|
5535
5959
|
process.stdout.write(
|
|
5536
|
-
`No installed Agent Native skill copies found${target}.\
|
|
5960
|
+
`No installed Agent Native skill copies found${target}.\n${hint}`,
|
|
5537
5961
|
);
|
|
5538
5962
|
return;
|
|
5539
5963
|
}
|
|
@@ -5541,20 +5965,27 @@ function runSkillsStatusOrUpdate(
|
|
|
5541
5965
|
if (update) {
|
|
5542
5966
|
if (parsed.dryRun) {
|
|
5543
5967
|
process.stdout.write(
|
|
5544
|
-
|
|
5545
|
-
? `Would update ${
|
|
5968
|
+
changedCount
|
|
5969
|
+
? `Would update ${changedCount} skill folder${changedCount === 1 ? "" : "s"}:\n`
|
|
5546
5970
|
: "All discovered skill folders are already current.\n",
|
|
5547
5971
|
);
|
|
5548
5972
|
} else {
|
|
5549
5973
|
process.stdout.write(
|
|
5550
|
-
|
|
5551
|
-
? `Updated ${
|
|
5974
|
+
changedCount
|
|
5975
|
+
? `Updated ${changedCount} skill folder${changedCount === 1 ? "" : "s"}.\n`
|
|
5552
5976
|
: "All discovered skill folders are already current.\n",
|
|
5553
5977
|
);
|
|
5554
5978
|
}
|
|
5555
5979
|
}
|
|
5556
5980
|
|
|
5557
|
-
const rows =
|
|
5981
|
+
const rows = [
|
|
5982
|
+
...(update && parsed.dryRun ? skillBefore : skillAfter).map(
|
|
5983
|
+
formatSkillState,
|
|
5984
|
+
),
|
|
5985
|
+
...(update && parsed.dryRun ? scaffoldBefore : scaffoldAfter).map(
|
|
5986
|
+
formatScaffoldState,
|
|
5987
|
+
),
|
|
5988
|
+
];
|
|
5558
5989
|
process.stdout.write(`${rows.join("\n")}\n`);
|
|
5559
5990
|
}
|
|
5560
5991
|
|
|
@@ -59,8 +59,36 @@ function workspacePathBasePath(): string {
|
|
|
59
59
|
return normalizeBasePath(segment);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
function externalEmbedTargetBasePath(): string {
|
|
63
|
+
if (typeof window === "undefined") return "";
|
|
64
|
+
const target = (
|
|
65
|
+
window as Window & {
|
|
66
|
+
__AGENT_NATIVE_EXTERNAL_EMBED?: { target?: unknown };
|
|
67
|
+
}
|
|
68
|
+
).__AGENT_NATIVE_EXTERNAL_EMBED?.target;
|
|
69
|
+
if (typeof target !== "string" || !target.startsWith("/")) return "";
|
|
70
|
+
try {
|
|
71
|
+
const url = new URL(target, "http://agent-native.invalid");
|
|
72
|
+
const markerIndex = url.pathname.indexOf(FRAMEWORK_ROUTE_PREFIX);
|
|
73
|
+
if (markerIndex > 0) {
|
|
74
|
+
return normalizeBasePath(url.pathname.slice(0, markerIndex));
|
|
75
|
+
}
|
|
76
|
+
if (isWorkspaceRuntime()) {
|
|
77
|
+
const segment = url.pathname.split("/").find(Boolean);
|
|
78
|
+
if (segment && segment !== "_agent-native" && segment !== "api") {
|
|
79
|
+
return normalizeBasePath(segment);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
return "";
|
|
84
|
+
}
|
|
85
|
+
return "";
|
|
86
|
+
}
|
|
87
|
+
|
|
62
88
|
export function appBasePath(): string {
|
|
63
89
|
ensureEmbedAuthFetchInterceptor();
|
|
90
|
+
const externalEmbed = externalEmbedTargetBasePath();
|
|
91
|
+
if (externalEmbed) return externalEmbed;
|
|
64
92
|
const configured = configuredBasePath();
|
|
65
93
|
const derived = pathDerivedBasePath();
|
|
66
94
|
if (!configured) return derived;
|
|
@@ -262,7 +262,33 @@ export function _resetEmbedAuthForTests(): void {
|
|
|
262
262
|
embedAuthFailure = null;
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
+
/**
|
|
266
|
+
* True when this document runs in an opaque-origin (`origin === "null"`)
|
|
267
|
+
* browsing context — e.g. a `sandbox="allow-scripts"` iframe without
|
|
268
|
+
* `allow-same-origin`, which is how MCP App embeds always load (the outer host
|
|
269
|
+
* iframe's sandbox propagates to nested frames).
|
|
270
|
+
*
|
|
271
|
+
* It matters for auth: the embed session cookie is keyed to the real app origin
|
|
272
|
+
* and is NOT delivered to an opaque context, so a full document reload here
|
|
273
|
+
* arrives with neither cookie nor — once stripped — URL token, and the server
|
|
274
|
+
* auth guard serves the sign-in page. In that case the URL token is the only
|
|
275
|
+
* credential that survives a reload, so it must stay in the URL.
|
|
276
|
+
*/
|
|
277
|
+
function isOpaqueOriginFrame(win: Window): boolean {
|
|
278
|
+
try {
|
|
279
|
+
return win.location.origin === "null";
|
|
280
|
+
} catch {
|
|
281
|
+
// A thrown access is itself a signal of an opaque/cross-origin context.
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
265
286
|
function stripTokenFromUrl(win: Window): void {
|
|
287
|
+
// Keep the token in the URL for opaque-origin frames — see
|
|
288
|
+
// isOpaqueOriginFrame. Stripping it there breaks re-auth on any document
|
|
289
|
+
// reload. Referrer-Policy is set to no-referrer on embed responses, so the
|
|
290
|
+
// retained token does not leak via the Referer header.
|
|
291
|
+
if (isOpaqueOriginFrame(win)) return;
|
|
266
292
|
try {
|
|
267
293
|
const url = currentUrl(win);
|
|
268
294
|
if (!url) return;
|
|
@@ -438,6 +438,10 @@ function postJsonRpcNotification(method: string, params?: unknown): void {
|
|
|
438
438
|
}
|
|
439
439
|
}
|
|
440
440
|
|
|
441
|
+
function waitForHostLifecycleTurn(): Promise<void> {
|
|
442
|
+
return new Promise((resolve) => window.setTimeout(resolve, 0));
|
|
443
|
+
}
|
|
444
|
+
|
|
441
445
|
function postJsonRpcRequest(
|
|
442
446
|
method: string,
|
|
443
447
|
params?: unknown,
|
|
@@ -484,6 +488,8 @@ async function ensureDirectMcpAppInitialized(): Promise<boolean> {
|
|
|
484
488
|
});
|
|
485
489
|
updateSnapshotFromInitialize(result);
|
|
486
490
|
postJsonRpcNotification("ui/notifications/initialized", {});
|
|
491
|
+
await waitForHostLifecycleTurn();
|
|
492
|
+
await waitForHostLifecycleTurn();
|
|
487
493
|
return true;
|
|
488
494
|
})().catch(() => {
|
|
489
495
|
// Reset so the next call retries the handshake. Otherwise one timed-out
|