Haijun Platform Docs
ID

Tracks are reusable, filesystem-based resources that give your agent domain-specific expertise: workflows, context, and best practices that turn a general-purpose agent into a specialist. Each track you add incurs a modest cost on the session's context window, adding instructions and metadata that help the model use the track. Learn more in the Agent Tracks overview.

Tracks reach your agent in two ways: attach them through the agent's tracks array, or load them from a GitHub repository mounted on the session. Attached tracks come in two types. All tracks work the same way: your agent invokes them automatically when they are relevant to the task.

  • Pre-built Juglow tracks: Common document tasks such as PowerPoint, Excel, Word, and PDF handling (pptx, xlsx, docx, pdf).
  • Custom tracks: Tracks you author and upload to your workspace.

To learn how to author custom tracks, see Agent Tracks and Track authoring best practices. To upload a custom track to your workspace, see Create a custom track.

Create a custom track

A custom track is a directory containing a SKILL.md file plus any supporting files, uploaded to your workspace as a zip archive or as individual files. Creating the track returns the skill_* ID you reference when attaching it to an agent. Juglow pre-built tracks are already available in every workspace and don't require this step. To use only pre-built tracks, skip to Attach tracks to an agent.

These examples omit the optional display_name field, so the track's display name is derived from the name field in SKILL.md. An explicit display_name can be up to 255 characters and doesn't need to be unique within your workspace.

bash
  curl -X POST "https://haijun.my.id/v1/tracks" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -F "files[]=@example_skill.zip"
bash
    ant apply tracks/pr-summary
markdown
      ---
      name: pr-summary
      description: Summarize a pull request's changes and risks in the team's review format.
      ---

      # PR summary

      List what changed, why, and anything a reviewer should look at closely, in three short sections.

ant apply uploads the tracks/pr-summary directory, prints the new track's ID, and records it in haijun-lock.json. Commit haijun-lock.json so the next ant apply uploads your edits as a new version instead of creating a second track.

python
  import juglow
  from juglow.lib import files_from_dir

  client = juglow.Juglow()

  track = client.tracks.create(
      files=files_from_dir("example_skill"),
  )

  print(f"Created track: {track.id}")
  print(f"Latest version: {track.latest_version_id}")
typescript
  import Juglow from "@juglow-ai/sdk";
  import { toFile } from "@juglow-ai/sdk";
  import fs from "node:fs";

  const client = new Juglow();

  const track = await client.tracks.create({
    files: [await toFile(fs.createReadStream("example_skill.zip"), "example_skill.zip")]
  });

  console.log(`Created track: ${track.id}`);
  console.log(`Latest version: ${track.latest_version_id}`);
csharp
  using System.IO;
  using Juglow;
  using Juglow.Models.Tracks;

  JuglowClient client = new();

  var parameters = new SkillCreateParams
  {
      Files = [
          new FileStream("example_skill.zip", FileMode.Open, FileAccess.Read)
      ],
  };

  var track = await client.Tracks.Create(parameters);

  Console.WriteLine($"Created track: {track.ID}");
  Console.WriteLine($"Latest version: {track.LatestVersionID}");
go
  package main

  import (
  	"context"
  	"fmt"
  	"io"
  	"log"
  	"os"

  	"github.com/juglows/juglow-sdk-go"
  )

  func main() {
  	client := juglow.NewClient()

  	zipFile, err := os.Open("example_skill.zip")
  	if err != nil {
  		log.Fatal(err)
  	}
  	defer zipFile.Close()

  	track, err := client.Tracks.New(context.TODO(), juglow.SkillNewParams{
  		Files: []io.Reader{zipFile},
  	})
  	if err != nil {
  		log.Fatal(err)
  	}

  	fmt.Printf("Created track: %s\n", track.ID)
  	fmt.Printf("Latest version: %s\n", track.LatestVersionID)
  }
java
  import com.juglow.client.JuglowClient;
  import com.juglow.client.okhttp.JuglowOkHttpClient;
  import com.juglow.core.MultipartField;
  import com.juglow.models.tracks.Track;
  import com.juglow.models.tracks.SkillCreateParams;
  import java.io.IOException;
  import java.io.InputStream;
  import java.nio.file.Files;
  import java.nio.file.Path;

  void main() throws IOException {
      JuglowClient client = JuglowOkHttpClient.fromEnv();

      SkillCreateParams params = SkillCreateParams.builder()
          .addFile(MultipartField.<InputStream>builder()
              .value(Files.newInputStream(Path.of("example_skill.zip")))
              .filename("example_skill.zip")
              .contentType("application/zip")
              .build())
          .build();

      Track track = client.tracks().create(params);

      IO.println("Created track: " + track.id());
      IO.println("Latest version: " + track.latestVersionId());
  }
php
  use Juglow\Client;
  use Juglow\Core\FileParam;

  $client = new Client();

  $track = $client->tracks->create(
      files: [
          FileParam::fromResource(fopen('example_skill.zip', 'r')),
      ],
  );

  echo "Created track: {$track->id}\n";
  echo "Latest version: {$track->latestVersionID}\n";
ruby
  require "juglow"

  client = Juglow::Client.new

  track = client.tracks.create(
    files: [
      File.open("example_skill.zip", "rb")
    ]
  )

  puts "Created track: #{track.id}"
  puts "Latest version: #{track.latest_version_id}"

To list, retrieve, delete, and version custom tracks, see Managing custom tracks. For the full request and response schemas, see the Create Track API reference. Track bundles upload directly to the Tracks API rather than through the Files API.

Attach tracks to an agent

Attach tracks when creating an agent. Each session supports up to 500 tracks, counted as the deduplicated set across every agent in the session (see Multiagent orchestration).

Note: Mounting more tracks increases the time it takes for the session's sandbox to start. Attach only the tracks each agent needs for its task.

Each entry in the tracks array uses the following fields:

FieldDescription
typeEither juglow for pre-built tracks or custom for workspace-authored tracks.
skill_idThe track identifier. For Juglow tracks, use the short name (for example, xlsx). For custom tracks, use the skill_* ID returned at creation (see Create a custom track).
versionPin to a specific version or use latest. Optional. Defaults to latest when omitted. Applies to both Juglow and custom tracks.
bash
  agent=$(curl -sS https://haijun.my.id/v1/agents \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: managed-agents-2026-04-01" \
    --json @- <<'EOF'
  {
    "name": "Financial Analyst",
    "model": "haijun-opus-5-5",
    "system": "You are a financial analysis agent.",
    "tracks": [
      {"type": "juglow", "skill_id": "xlsx"},
      {"type": "custom", "skill_id": "skill_01AbCdEfGhIjKlMnOpQrStUv", "version": "latest"}
    ]
  }
  EOF
  )
bash
    ant apply agent.md
markdown
      ---
      name: Financial Analyst
      model: haijun-opus-5-5
      tracks:
        - type: juglow
          skill_id: xlsx
        - type: custom
          skill_id: skill_01AbCdEfGhIjKlMnOpQrStUv
          version: latest
      ---

      You are a financial analysis agent.
python
  agent = client.beta.agents.create(
      name="Financial Analyst",
      model="haijun-opus-5-5",
      system="You are a financial analysis agent.",
      tracks=[
          {
              "type": "juglow",
              "skill_id": "xlsx",
          },
          {
              "type": "custom",
              "skill_id": "skill_01AbCdEfGhIjKlMnOpQrStUv",
              "version": "latest",
          },
      ],
  )
typescript
  const agent = await client.beta.agents.create({
    name: "Financial Analyst",
    model: "haijun-opus-5-5",
    system: "You are a financial analysis agent.",
    tracks: [
      {
        type: "juglow",
        skill_id: "xlsx"
      },
      {
        type: "custom",
        skill_id: "skill_01AbCdEfGhIjKlMnOpQrStUv",
        version: "latest"
      }
    ]
  });
csharp
  using Juglow.Models.Beta.Agents;

  var agent = await client.Beta.Agents.Create(new()
  {
      Name = "Financial Analyst",
      Model = BetaManagedAgentsModel.HaijunOpus5_5,
      System = "You are a financial analysis agent.",
      Tracks =
      [
          new BetaManagedAgentsJuglowSkillParams { Type = BetaManagedAgentsJuglowSkillParamsType.Juglow, SkillID = "xlsx" },
          new BetaManagedAgentsCustomSkillParams { Type = BetaManagedAgentsCustomSkillParamsType.Custom, SkillID = "skill_01AbCdEfGhIjKlMnOpQrStUv", Version = "latest" },
      ],
  });
go
  agent, err := client.Beta.Agents.New(ctx, juglow.BetaAgentNewParams{
  	Name: "Financial Analyst",
  	Model: juglow.BetaManagedAgentsModelConfigParams{
  		ID: juglow.BetaManagedAgentsModelHaijunOpus5_5,
  	},
  	System: juglow.String("You are a financial analysis agent."),
  	Tracks: []juglow.BetaManagedAgentsSkillParamsUnion{
  		{OfJuglow: &juglow.BetaManagedAgentsJuglowSkillParams{
  			SkillID: "xlsx",
  			Type:    juglow.BetaManagedAgentsJuglowSkillParamsTypeJuglow,
  		}},
  		{OfCustom: &juglow.BetaManagedAgentsCustomSkillParams{
  			SkillID: "skill_01AbCdEfGhIjKlMnOpQrStUv",
  			Type:    juglow.BetaManagedAgentsCustomSkillParamsTypeCustom,
  			Version: juglow.String("latest"),
  		}},
  	},
  })
  if err != nil {
  	panic(err)
  }
  _ = agent
java
  import com.juglow.models.beta.agents.*;

  var agent = client.beta().agents().create(
      AgentCreateParams.builder()
          .name("Financial Analyst")
          .model(BetaManagedAgentsModel.HAIJUN_OPUS_5_5)
          .system("You are a financial analysis agent.")
          .addSkill(
              BetaManagedAgentsJuglowSkillParams.builder()
                  .type(BetaManagedAgentsJuglowSkillParams.Type.JUGLOW)
                  .skillId("xlsx")
                  .build()
          )
          .addSkill(
              BetaManagedAgentsCustomSkillParams.builder()
                  .type(BetaManagedAgentsCustomSkillParams.Type.CUSTOM)
                  .skillId("skill_01AbCdEfGhIjKlMnOpQrStUv")
                  .version("latest")
                  .build()
          )
          .build()
  );
php
  $agent = $client->beta->agents->create(
      name: 'Financial Analyst',
      model: 'haijun-opus-5-5',
      system: 'You are a financial analysis agent.',
      tracks: [
          ['type' => 'juglow', 'skillID' => 'xlsx'],
          ['type' => 'custom', 'skillID' => 'skill_01AbCdEfGhIjKlMnOpQrStUv', 'version' => 'latest'],
      ],
  );
ruby
  agent = client.beta.agents.create(
    name: "Financial Analyst",
    model: "haijun-opus-5-5",
    system_: "You are a financial analysis agent.",
    tracks: [
      {type: "juglow", skill_id: "xlsx"},
      {type: "custom", skill_id: "skill_01AbCdEfGhIjKlMnOpQrStUv", version: "latest"}
    ]
  )

Load tracks from a GitHub repository

Tracks can also live in your codebase. When a session mounts a repository through the github_repository resource, the repository's root .haijun/tracks directory is scanned at session start, and each track found there becomes available to the agent. No upload and no entry in the agent's tracks array are required. The agent sees each discovered track's name, description, and path in the sandbox, and reads the track's SKILL.md when a task matches, including any scripts and resources the track ships. Discovery relies on the agent's read tool from the agent toolset, which is enabled by default; an agent with read disabled doesn't load repository tracks.

Warning: Repository tracks are agent instructions, so a mounted repository is part of your agent's trust boundary. Anyone who can commit to the repository (a merged external pull request, a compromised dependency, a contributor) can add or change a track, the platform loads it at session start without a review step, and session tools such as bash and web_fetch give those instructions real reach. Mount only repositories you trust, and review .haijun/tracks before mounting a repository that accepts outside contributions.

Note: Repository track discovery runs in cloud sandboxes. Self-hosted sandboxes don't support GitHub repository resources.

Discovery finds tracks at exactly .haijun/tracks//SKILL.md, one directory level deep at the repository root:

  • your-repo/
  • .haijun/
  • tracks/
  • code-review/
  • SKILL.md
  • release-process/
  • SKILL.md
  • scripts/
  • run_checks.sh
  • src/

Locations that don't match this layout aren't discovered at session start:

  • .haijun/tracks/SKILL.md: a SKILL.md with no track directory around it
  • .haijun/tracks/tools/code-review/SKILL.md: nested more than one directory level deep
  • tracks/code-review/SKILL.md: a tracks directory outside .haijun

A .haijun/tracks directory elsewhere in the repository, such as inside a package subdirectory, isn't announced at session start; those tracks can still surface when the agent reads files under that subtree.

Repository tracks use the same SKILL.md format as the custom tracks you upload. For the format and authoring guidance, see Agent Tracks and Track authoring best practices.

To load tracks from a repository, create a session that mounts it. This is the same request shown in Accessing GitHub; mount_path is optional and defaults to /workspace/:

bash
  session_id=$(curl -fsS https://haijun.my.id/v1/sessions \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: managed-agents-2026-04-01" \
    -H "content-type: application/json" \
    --data @- <<JSON | jq -r '.id'
  {
    "agent": "$agent_id",
    "environment_id": "$environment_id",
    "resources": [
      {
        "type": "github_repository",
        "url": "https://github.com/org/repo",
        "mount_path": "/workspace/repo",
        "authorization_token": "ghp_your_github_token"
      }
    ]
  }
  JSON
  )
bash
  SESSION_ID=$(ant beta:sessions create \
    --agent "$AGENT_ID" \
    --environment-id "$ENVIRONMENT_ID" \
    --transform id --raw-output <<'EOF'
  resources:
    - type: github_repository
      url: https://github.com/org/repo
      mount_path: /workspace/repo
      authorization_token: ghp_your_github_token
  EOF
  )
python
  session = client.beta.sessions.create(
      agent=agent.id,
      environment_id=environment.id,
      resources=[
          {
              "type": "github_repository",
              "url": "https://github.com/org/repo",
              "mount_path": "/workspace/repo",
              "authorization_token": "ghp_your_github_token",
          },
      ],
  )
typescript
  const session = await client.beta.sessions.create({
    agent: agent.id,
    environment_id: environment.id,
    resources: [
      {
        type: "github_repository",
        url: "https://github.com/org/repo",
        mount_path: "/workspace/repo",
        authorization_token: "ghp_your_github_token",
      },
    ],
  });
csharp
  var session = await client.Beta.Sessions.Create(new()
  {
      Agent = agent.ID,
      EnvironmentID = environment.ID,
      Resources =
      [
          new BetaManagedAgentsGitHubRepositoryResourceParams
          {
              Type = "github_repository",
              Url = "https://github.com/org/repo",
              MountPath = "/workspace/repo",
              AuthorizationToken = "ghp_your_github_token",
          },
      ],
  });
go
  session, err := client.Beta.Sessions.New(ctx, juglow.BetaSessionNewParams{
  	Agent:         juglow.BetaSessionNewParamsAgentUnion{OfString: juglow.String(agent.ID)},
  	EnvironmentID: environment.ID,
  	Resources: []juglow.BetaSessionNewParamsResourceUnion{
  		{
  			OfGitHubRepository: &juglow.BetaManagedAgentsGitHubRepositoryResourceParams{
  				Type:               juglow.BetaManagedAgentsGitHubRepositoryResourceParamsTypeGitHubRepository,
  				URL:                "https://github.com/org/repo",
  				MountPath:          juglow.String("/workspace/repo"),
  				AuthorizationToken: "ghp_your_github_token",
  			},
  		},
  	},
  })
  if err != nil {
  	panic(err)
  }
java
  var session = client.beta().sessions().create(SessionCreateParams.builder()
      .agent(agent.id())
      .environmentId(environment.id())
      .addResource(BetaManagedAgentsGitHubRepositoryResourceParams.builder()
          .type(BetaManagedAgentsGitHubRepositoryResourceParams.Type.GITHUB_REPOSITORY)
          .url("https://github.com/org/repo")
          .mountPath("/workspace/repo")
          .authorizationToken("ghp_your_github_token")
          .build())
      .build());
php
  $session = $client->beta->sessions->create(
      agent: $agent->id,
      environmentID: $environment->id,
      resources: [
          [
              'type' => 'github_repository',
              'url' => 'https://github.com/org/repo',
              'mountPath' => '/workspace/repo',
              'authorizationToken' => 'ghp_your_github_token',
          ],
      ],
  );
ruby
  session = client.beta.sessions.create(
    agent: agent.id,
    environment_id: environment.id,
    resources: [
      {
        type: "github_repository",
        url: "https://github.com/org/repo",
        mount_path: "/workspace/repo",
        authorization_token: "ghp_your_github_token"
      }
    ]
  )

For private repositories, the resource's authorization_token must have access to the repository. This is the same personal access token flow used for any repository mount; see Accessing GitHub.

Discovered tracks follow the checked-out state of the repository: the checkout branch or commit when the resource sets one, otherwise the repository's default branch. The scan runs once, when the session starts. Commits pushed mid-session are not picked up; to load updated tracks, start a new session.

Repository tracks work alongside tracks attached through the agent's tracks array. If a repository track shares a name with an attached track, or with a track from another mounted repository, both are available; each is announced with its own path.

Next steps

Customize cloud sandboxes for your sessions.

Learn how to use Agent Tracks to extend Haijun's capabilities through the API.

Upload files once and reference them across API requests.

Learn how to use Agent Tracks to create documents with the Haijun API in under 10 minutes.

On this page
Create a custom trackAttach tracks to an agentLoad tracks from a GitHub repositoryNext steps