Haijun Platform Docs
EN

Sebuah scheduled deployment (deployment terjadwal) memungkinkan agen untuk memulai sesi secara otonom, sehingga tugas dapat diselesaikan dalam irama yang dapat diprediksi. Anda membuat dan mengelola deployment dengan Deployments API, bagian dari Haijun API.

Untuk konteks peluncuran dan contoh apa yang dijalankan tim secara terjadwal, lihat deployment terjadwal dan vault di Haijun Managed Agents di blog.

Membuat deployment terjadwal

Saat membuat deployment, Anda meneruskan konfigurasi sesi yang diperlukan untuk eksekusi, selain sebuah schedule.

  • Deployment memerlukan konfigurasi agen dan konfigurasi environment, serta secara opsional menerima file, GitHub, memory store, dan vault. Deployment yang menargetkan environment self-hosted dapat melampirkan memory store; resource file dan github_repository memerlukan environment cloud. Formulir deployment di Haijun Console saat ini tidak menawarkan memory store untuk environment self-hosted; sebagai gantinya, lampirkan melalui API atau SDK.
  • Deployment juga memerlukan setidaknya satu event awal, yaitu user.message atau user.define_outcome, yang memulai pekerjaan setiap sesi. Dalam file deployment untuk ant apply, teks di bawah frontmatter menjadi user.message tersebut.
  • Dalam schedule, Anda mendefinisikan expression cron dan timezone. Granularitas maksimum yang didukung adalah tingkat menit.
bash
  curl --fail-with-body -sS "https://haijun.my.id/v1/deployments?beta=true" \
    -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" \
    -d @- <<EOF
  {
    "name": "Weekly compliance scan",
    "agent": "$AGENT_ID",
    "environment_id": "$ENVIRONMENT_ID",
    "initial_events": [
      {"type": "user.message", "content": [{"type": "text", "text": "Run the weekly compliance scan."}]}
    ],
    "schedule": {
      "type": "cron",
      "expression": "0 20 * * 5",
      "timezone": "America/New_York"
    }
  }
  EOF
bash
    ant apply deployment.md
markdown
      ---
      name: Weekly compliance scan
      agent: agent_011CYm1BLqPXpQRk5khsSXrs
      environment_id: env_01595EKxaaTTGwwY3kyXdtbs
      schedule:
        type: cron
        expression: "0 20 * * 5"
        timezone: America/New_York
      ---

      Run the weekly compliance scan.

ant apply mencetak ID deployment baru dan mencatatnya di haijun-lock.json. Untuk melihat objek deployment, jalankan ant beta:deployments retrieve.

python
  deployment = client.beta.deployments.create(
      name="Weekly compliance scan",
      agent=agent.id,
      environment_id=environment.id,
      initial_events=[
          {
              "type": "user.message",
              "content": [{"type": "text", "text": "Run the weekly compliance scan."}],
          },
      ],
      schedule={
          "type": "cron",
          "expression": "0 20 * * 5",
          "timezone": "America/New_York",
      },
  )
typescript
  const deployment = await client.beta.deployments.create({
    name: "Weekly compliance scan",
    agent: agent.id,
    environment_id: environment.id,
    initial_events: [
      {
        type: "user.message",
        content: [{ type: "text", text: "Run the weekly compliance scan." }],
      },
    ],
    schedule: {
      type: "cron",
      expression: "0 20 * * 5",
      timezone: "America/New_York",
    },
  });
csharp
  var deployment = await client.Beta.Deployments.Create(new()
  {
      Name = "Weekly compliance scan",
      Agent = agent.ID,
      EnvironmentID = environment.ID,
      InitialEvents =
      [
          new BetaManagedAgentsUserMessageEventParams
          {
              Type = BetaManagedAgentsUserMessageEventParamsType.UserMessage,
              Content =
              [
                  new BetaManagedAgentsTextBlock
                  {
                      Type = BetaManagedAgentsTextBlockType.Text,
                      Text = "Run the weekly compliance scan.",
                  },
              ],
          },
      ],
      Schedule = new BetaManagedAgentsScheduleParams
      {
          Type = BetaManagedAgentsScheduleParamsType.Cron,
          Expression = "0 20 * * 5",
          Timezone = "America/New_York",
      },
  });
go
  deployment, err := client.Beta.Deployments.New(ctx, juglow.BetaDeploymentNewParams{
  	Name:          "Weekly compliance scan",
  	Agent:         juglow.BetaDeploymentNewParamsAgentUnion{OfString: juglow.String(agent.ID)},
  	EnvironmentID: environment.ID,
  	InitialEvents: []juglow.BetaManagedAgentsDeploymentInitialEventParamsUnion{{
  		OfUserMessage: &juglow.BetaManagedAgentsUserMessageEventParams{
  			Type: juglow.BetaManagedAgentsUserMessageEventParamsTypeUserMessage,
  			Content: []juglow.BetaManagedAgentsUserMessageEventParamsContentUnion{{
  				OfText: &juglow.BetaManagedAgentsTextBlockParam{
  					Type: juglow.BetaManagedAgentsTextBlockTypeText,
  					Text: "Run the weekly compliance scan.",
  				},
  			}},
  		},
  	}},
  	Schedule: juglow.BetaManagedAgentsScheduleParams{
  		Type:       juglow.BetaManagedAgentsScheduleParamsTypeCron,
  		Expression: "0 20 * * 5",
  		Timezone:   "America/New_York",
  	},
  })
  if err != nil {
  	panic(err)
  }
java
  var deployment = client.beta().deployments().create(
      DeploymentCreateParams.builder()
          .name("Weekly compliance scan")
          .agent(agent.id())
          .environmentId(environment.id())
          .addInitialEvent(
              BetaManagedAgentsUserMessageEventParams.builder()
                  .type(BetaManagedAgentsUserMessageEventParams.Type.USER_MESSAGE)
                  .addTextContent("Run the weekly compliance scan.")
                  .build()
          )
          .schedule(
              BetaManagedAgentsScheduleParams.builder()
                  .type(BetaManagedAgentsScheduleParams.Type.CRON)
                  .expression("0 20 * * 5")
                  .timezone("America/New_York")
                  .build()
          )
          .build()
  );
php
  $deployment = $client->beta->deployments->create(
      name: 'Weekly compliance scan',
      agent: $agent->id,
      environmentID: $environment->id,
      initialEvents: [
          [
              'type' => 'user.message',
              'content' => [['type' => 'text', 'text' => 'Run the weekly compliance scan.']],
          ],
      ],
      schedule: [
          'type' => 'cron',
          'expression' => '0 20 * * 5',
          'timezone' => 'America/New_York',
      ],
  );
ruby
  deployment = client.beta.deployments.create(
    name: "Weekly compliance scan",
    agent: agent.id,
    environment_id: environment.id,
    initial_events: [
      {
        type: "user.message",
        content: [{type: "text", text: "Run the weekly compliance scan."}]
      }
    ],
    schedule: {
      type: "cron",
      expression: "0 20 * * 5",
      timezone: "America/New_York"
    }
  )

Respons mencakup objek deployment dengan schedule.upcoming_runs_at yang terisi dengan waktu eksekusi berikutnya, untuk mengonfirmasi bahwa jadwal Anda telah ditetapkan dengan benar.

json
{
  "id": "depl_01xyz",
  "status": "active",
  "paused_reason": null,
  "schedule": {
    "type": "cron",
    "expression": "0 20 * * 5",
    "timezone": "America/New_York",
    "last_run_at": null,
    "upcoming_runs_at": [
      "2026-05-09T00:00:00Z",
      "2026-05-16T00:00:00Z",
      "2026-05-23T00:00:00Z"
    ]
  }
}

Timestamp eksekusi mendatang mencerminkan jadwal persis yang dikonfigurasi. Namun, untuk mendistribusikan beban, eksekusi aktual menerapkan jitter hingga 15% dari interval antar eksekusi, dengan minimum 5 detik dan maksimum 9 menit.

Maksimum 1.000 deployment terjadwal didukung per organisasi. Hubungi dukungan Juglow jika Anda membutuhkan lebih banyak.

Lihat referensi Create Deployment untuk parameter lengkap dan skema respons.

Semantik cron dan zona waktu

  • Expression: Cron POSIX standar (minute hour day-of-month month day-of-week). Anda dapat membuat dan memvalidasi ekspresi cron ini di Haijun Console.
  • Timezone: Pengidentifikasi zona waktu IANA (misalnya, "America/Los_Angeles").
  • DST: Jadwal cron menggunakan pencocokan waktu jam dinding secara literal, sehingga "0 20 * * *" di America/New_York dijalankan pada pukul 20.00 waktu setempat terlepas dari apakah EST atau EDT yang sedang berlaku.

Note: Waktu jam dinding yang tidak ada pada hari pemajuan jam (seperti pukul 2 pagi) tidak dipicu. Waktu jam dinding yang terjadi dua kali pada hari pemunduran jam dijalankan dua kali. Jadwalkan di luar rentang pukul 1–3 pagi waktu setempat, atau gunakan UTC, jika eksekusi yang terlewat atau duplikat tidak dapat diterima.

Menetapkan anggaran pada setiap eksekusi

Teruskan objek budget opsional saat Anda membuat atau memperbarui deployment. Objek ini memiliki bentuk yang sama dengan anggaran sesi. Deployment menyalin batas tersebut ke setiap sesi yang dimulainya, sehingga anggaran membatasi setiap eksekusi secara terpisah alih-alih bertindak sebagai plafon kumulatif di seluruh eksekusi: deployment dengan batas "2000" dapat membelanjakan hingga sekitar $20 pada setiap eksekusi.

Sesi yang dimulai oleh deployment berperilaku persis seperti sesi beranggaran lainnya: sesi tersebut dijeda dengan budget_reached ketika biaya daftarnya sendiri mencapai batas. Mengubah anggaran deployment berlaku untuk eksekusi yang dimulai setelahnya; sesi yang sudah berjalan mempertahankan batas yang dimilikinya saat dimulai, yang dapat Anda ubah melalui sesi itu sendiri. Tidak seperti anggaran sesi, anggaran deployment dapat dihapus dengan "budget": null dan ditetapkan kembali nanti.

Contoh berikut menetapkan anggaran pada deployment yang sudah ada:

bash
curl --fail-with-body -sS "https://haijun.my.id/v1/deployments/$DEPLOYMENT_ID?beta=true" \
  -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" \
  -d @- <<'EOF'
{
  "budget": {
    "type": "limit",
    "max_list_cost": {"amount": "2000", "currency": "USD"}
  }
}
EOF

Eksekusi deployment

Deployment dapat gagal dipicu karena berbagai alasan: misalnya, jika resource environment telah diarsipkan, atau jika pembuatan sesi terkena batas laju. Setiap upaya mengeksekusi deployment menghasilkan catatan deployment run (eksekusi deployment), yang memungkinkan Anda melacak keberhasilan dan kegagalan secara independen dari siklus hidup sesi.

Deployment yang berhasil menghasilkan sesi aktif, dan eksekusi deployment yang berhasil berisi session_id terkait. Untuk mengikuti siklus hidup sesi, lacak event sesi melalui event stream atau webhook. Perubahan siklus hidup deployment dan hasil dari setiap eksekusi terjadwal juga dikirimkan sebagai event webhook, yang tercantum di tab Deployment events dan Deployment run events pada Jenis event yang didukung.

Daftarkan semua eksekusi deployment untuk sebuah deployment sebagai berikut:

bash
  curl --fail-with-body -sS "https://haijun.my.id/v1/deployment_runs?beta=true&deployment_id=$DEPLOYMENT_ID" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: managed-agents-2026-04-01"
bash
  ant beta:deployment-runs list --deployment-id "$DEPLOYMENT_ID"
python
  for run in client.beta.deployment_runs.list(
      deployment_id=deployment.id,
  ):
      print(run.created_at, run.session_id or run.error.type)
typescript
  for await (const run of client.beta.deploymentRuns.list({
    deployment_id: deployment.id,
  })) {
    console.log(run.created_at, run.session_id ?? run.error?.type);
  }
csharp
  var runs = await client.Beta.DeploymentRuns.List(
      new() { DeploymentID = deployment.ID }
  );
  await foreach (var run in runs.Paginate())
  {
      // Union Error mengekspos .Message secara langsung; diskriminatornya dibaca
      // dari .Json sampai accessor .Type yang umum ditambahkan.
      var outcome = run.SessionID ?? run.Error!.Json.GetProperty("type").GetString();
      Console.WriteLine($"{run.CreatedAt} {outcome}");
  }
go
  runs := client.Beta.DeploymentRuns.ListAutoPaging(ctx, juglow.BetaDeploymentRunListParams{
  	DeploymentID: juglow.String(deployment.ID),
  })
  for runs.Next() {
  	run := runs.Current()
  	if run.SessionID != "" {
  		fmt.Println(run.CreatedAt.Format(time.RFC3339), run.SessionID)
  	} else {
  		fmt.Println(run.CreatedAt.Format(time.RFC3339), run.Error.Type)
  	}
  }
  if err := runs.Err(); err != nil {
  	panic(err)
  }
java
  for (var run : client.beta().deploymentRuns().list(
          DeploymentRunListParams.builder()
              .deploymentId(deployment.id())
              .build()).autoPager()) {
      // Union Error belum mengekspos accessor umum .type()/.message();
      // .toString() menyertakan keduanya.
      IO.println(run.createdAt() + " "
          + run.sessionId().orElseGet(() -> run.error().orElseThrow().toString()));
  }
php
  foreach ($client->beta->deploymentRuns->list(
      deploymentID: $deployment->id,
  )->pagingEachItem() as $run) {
      $outcome = $run->sessionID ?? $run->error->type;
      echo "{$run->createdAt->format(DATE_ATOM)} {$outcome}\n";
  }
ruby
  client.beta.deployment_runs.list(
    deployment_id: deployment.id
  ).auto_paging_each do
    puts "#{it.created_at} #{it.session_id || it.error.type}"
  end

Anda juga dapat memfilter eksekusi deployment yang memiliki error:

bash
  curl --fail-with-body -sS "https://haijun.my.id/v1/deployment_runs?beta=true&deployment_id=$DEPLOYMENT_ID&has_error=true" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: managed-agents-2026-04-01"
bash
  ant beta:deployment-runs list --deployment-id "$DEPLOYMENT_ID" --has-error
python
  for run in client.beta.deployment_runs.list(
      deployment_id=deployment.id,
      has_error=True,
  ):
      print(run.created_at, run.error.type, run.error.message)
typescript
  for await (const run of client.beta.deploymentRuns.list({
    deployment_id: deployment.id,
    has_error: true,
  })) {
    console.log(run.created_at, run.error?.type, run.error?.message);
  }
csharp
  var failedRuns = await client.Beta.DeploymentRuns.List(
      new() { DeploymentID = deployment.ID, HasError = true }
  );
  await foreach (var failedRun in failedRuns.Paginate())
  {
      var error = failedRun.Error!;
      var errorType = error.Json.GetProperty("type").GetString();
      Console.WriteLine($"{failedRun.CreatedAt} {errorType} {error.Message}");
  }
go
  failedRuns := client.Beta.DeploymentRuns.ListAutoPaging(ctx, juglow.BetaDeploymentRunListParams{
  	DeploymentID: juglow.String(deployment.ID),
  	HasError:     juglow.Bool(true),
  })
  for failedRuns.Next() {
  	failedRun := failedRuns.Current()
  	fmt.Println(failedRun.CreatedAt.Format(time.RFC3339), failedRun.Error.Type, failedRun.Error.Message)
  }
  if err := failedRuns.Err(); err != nil {
  	panic(err)
  }
java
  for (var run : client.beta().deploymentRuns().list(
          DeploymentRunListParams.builder()
              .deploymentId(deployment.id())
              .hasError(true)
              .build()).autoPager()) {
      IO.println(run.createdAt() + " " + run.error().orElseThrow());
  }
php
  foreach ($client->beta->deploymentRuns->list(
      deploymentID: $deployment->id,
      hasError: true,
  )->pagingEachItem() as $run) {
      echo "{$run->createdAt->format(DATE_ATOM)} {$run->error->type} {$run->error->message}\n";
  }
ruby
  client.beta.deployment_runs.list(
    deployment_id: deployment.id,
    has_error: true
  ).auto_paging_each do
    puts "#{it.created_at} #{it.error.type} #{it.error.message}"
  end

Eksekusi yang gagal mencakup error dengan type yang menjelaskan mengapa pembuatan sesi ditolak (misalnya, environment_archived_error, agent_archived_error, atau session_rate_limited_error). Lihat referensi List Deployment Runs untuk semua parameter filter dan skema respons.

json
{
  "type": "deployment_run",
  "id": "drun_01abc124",
  "deployment_id": "depl_01xyz",
  "trigger_context": { "type": "schedule", "scheduled_at": "2026-05-09T00:00:00Z" },
  "session_id": null,
  "error": {
    "type": "environment_archived_error",
    "message": "environment `env_01abc` is archived"
  },
  "agent": { "type": "agent", "id": "agent_01ghi789", "version": 3 },
  "created_at": "2026-05-09T00:00:01Z"
}

Untuk mengambil satu eksekusi berdasarkan ID, panggil GET /v1/deployment_runs/{deployment_run_id}. Sebuah event webhook deployment_run membawa ID eksekusi sebagai data.id-nya.

Mengelola siklus hidup deployment

Setiap perubahan siklus hidup memancarkan event webhook, sehingga Anda dapat bereaksi terhadap deployment yang dijeda, dilanjutkan, atau diarsipkan tanpa polling; lihat tab Deployment events.

Pause menekan pemicu terjadwal untuk ke depannya; sesi yang sedang berjalan dari eksekusi deployment sebelumnya tetap dieksekusi. Eksekusi manual melalui endpoint run masih diizinkan saat dijeda. Menjeda menetapkan paused_reason ke {"type": "manual"}; melanjutkan akan menghapusnya.

bash
  curl --fail-with-body -sS -X POST "https://haijun.my.id/v1/deployments/$DEPLOYMENT_ID/pause?beta=true" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: managed-agents-2026-04-01"
bash
  ant beta:deployments pause --deployment-id "$DEPLOYMENT_ID"
python
  client.beta.deployments.pause(deployment.id)
typescript
  await client.beta.deployments.pause(deployment.id);
csharp
  await client.Beta.Deployments.Pause(deployment.ID);
go
  if _, err := client.Beta.Deployments.Pause(ctx, deployment.ID, juglow.BetaDeploymentPauseParams{}); err != nil {
  	panic(err)
  }
java
  client.beta().deployments().pause(deployment.id());
php
  $client->beta->deployments->pause($deployment->id);
ruby
  client.beta.deployments.pause(deployment.id)

Unpause melanjutkan jadwal dari kejadian terjadwal berikutnya. Pemicu yang terlewat tidak diisi ulang.

bash
  curl --fail-with-body -sS -X POST "https://haijun.my.id/v1/deployments/$DEPLOYMENT_ID/unpause?beta=true" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: managed-agents-2026-04-01"
bash
  ant beta:deployments unpause --deployment-id "$DEPLOYMENT_ID"
python
  client.beta.deployments.unpause(deployment.id)
typescript
  await client.beta.deployments.unpause(deployment.id);
csharp
  await client.Beta.Deployments.Unpause(deployment.ID);
go
  if _, err := client.Beta.Deployments.Unpause(ctx, deployment.ID, juglow.BetaDeploymentUnpauseParams{}); err != nil {
  	panic(err)
  }
java
  client.beta().deployments().unpause(deployment.id());
php
  $client->beta->deployments->unpause($deployment->id);
ruby
  client.beta.deployments.unpause(deployment.id)

Archive, tidak seperti pause, bersifat terminal: jadwal berakhir dan deployment tidak dapat dimodifikasi.

bash
  curl --fail-with-body -sS -X POST "https://haijun.my.id/v1/deployments/$DEPLOYMENT_ID/archive?beta=true" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: managed-agents-2026-04-01"
bash
  ant beta:deployments archive --deployment-id "$DEPLOYMENT_ID"
python
  client.beta.deployments.archive(deployment.id)
typescript
  await client.beta.deployments.archive(deployment.id);
csharp
  await client.Beta.Deployments.Archive(deployment.ID);
go
  if _, err := client.Beta.Deployments.Archive(ctx, deployment.ID, juglow.BetaDeploymentArchiveParams{}); err != nil {
  	panic(err)
  }
java
  client.beta().deployments().archive(deployment.id());
php
  $client->beta->deployments->archive($deployment->id);
ruby
  client.beta.deployments.archive(deployment.id)

Perilaku kegagalan

Respons batas laju pembuatan sesi langsung dicatat sebagai eksekusi session_rate_limited_error tanpa percobaan ulang; jadwal akan mencoba lagi pada kejadian terjadwal berikutnya. Batas laju pada panggilan API yang mendasari di dalam sesi ditangani oleh sesi itu sendiri.

Jika agen dari sebuah deployment telah diarsipkan, deployment tersebut secara otomatis diarsipkan dalam operasi yang sama. Jika agen telah dihapus, pemicu terjadwal berikutnya mendeteksi agen yang hilang dan secara otomatis mengarsipkan deployment. Dalam kedua kasus tersebut, tidak ada eksekusi deployment yang dicatat. Jika subagen yang direferensikan oleh agen telah diarsipkan, pemicu berikutnya mencatat eksekusi gagal dengan error.type: "agent_archived_error" dan deployment secara otomatis dijeda sehingga Anda dapat memperbarui agen dan melanjutkannya. Error pembuatan sesi lain yang tidak dapat dipulihkan, seperti environment atau vault yang diarsipkan, berperilaku dengan cara yang sama: pemicu mencatat eksekusi gagal dan deployment secara otomatis dijeda. paused_reason.error.type milik deployment mencerminkan error.type dari eksekusi yang gagal.

Memicu eksekusi manual

Untuk menjalankan deployment di luar jadwalnya, panggil endpoint run. Ini langsung membuat sesi dan menulis eksekusi deployment dengan trigger_context.type: "manual". Ini memungkinkan Anda menguji deployment sebelum berkomitmen pada jadwal.

bash
  curl --fail-with-body -sS -X POST "https://haijun.my.id/v1/deployments/$DEPLOYMENT_ID/run?beta=true" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: managed-agents-2026-04-01"
bash
  ant beta:deployments run --deployment-id "$DEPLOYMENT_ID"
python
  run = client.beta.deployments.run(deployment.id)
typescript
  const run = await client.beta.deployments.run(deployment.id);
csharp
  var manualRun = await client.Beta.Deployments.Run(deployment.ID);
go
  manualRun, err := client.Beta.Deployments.Run(ctx, deployment.ID, juglow.BetaDeploymentRunParams{})
  if err != nil {
  	panic(err)
  }
java
  var run = client.beta().deployments().run(deployment.id());
php
  $run = $client->beta->deployments->run($deployment->id);
ruby
  run = client.beta.deployments.run(deployment.id)
On this page
Membuat deployment terjadwalSemantik cron dan zona waktuMenetapkan anggaran pada setiap eksekusiEksekusi deploymentMengelola siklus hidup deploymentPerilaku kegagalanMemicu eksekusi manual