Haijun Platform Docs
EN

Orkestrasi multiagen memungkinkan satu agen berkoordinasi dengan agen lain untuk menyelesaikan pekerjaan yang kompleks. Agen dapat bertindak secara paralel dengan konteks terisolasi masing-masing, yang membantu meningkatkan kualitas output dan juga dapat mempercepat waktu penyelesaian.

Tidak yakin apakah pengaturan multiagen cocok untuk masalah Anda? Lihat kapan menggunakan sistem multiagen (dan kapan tidak).

Cara kerjanya

Semua agen berbagi sandbox, filesystem, dan kredensial vault yang sama, tetapi setiap agen berjalan dalam session thread (thread sesi) miliknya sendiri, yaitu aliran event yang terisolasi konteksnya dengan riwayat percakapannya sendiri. Koordinator melaporkan aktivitas di primary thread (thread utama), yang sama dengan aliran event tingkat sesi; thread tambahan dibuat saat runtime ketika koordinator mendelegasikan pekerjaan.

Thread bersifat persisten: koordinator dapat mengirim tindak lanjut ke agen yang dipanggilnya sebelumnya, dan agen tersebut mempertahankan semua hal dari giliran sebelumnya.

Setiap agen menggunakan konfigurasinya sendiri: model, prompt sistem, alat, server MCP, dan track. Override konfigurasi agen tingkat sesi adalah pengecualian; override tersebut berlaku untuk koordinator dan salinan self-nya. Alat, server MCP, dan konteks tidak dibagikan.

Apa yang perlu didelegasikan

Koordinasi multiagen paling cocok untuk tugas kompleks yang memerlukan pekerjaan di berbagai permukaan, atau di mana beberapa tugas dengan cakupan yang jelas berkontribusi pada tujuan keseluruhan.

Pola yang bekerja dengan baik:

  • Paralelisasi: Sebarkan subtugas independen secara bersamaan (mencari di beberapa sumber, menganalisis file terpisah) dan minta koordinator mensintesis hasilnya.
  • Spesialisasi: Arahkan ke agen dengan prompt sistem dan alat yang berfokus pada domain tertentu, seperti agen keamanan atau agen dokumentasi, daripada membebani satu agen dengan semua kemampuan.
  • Eskalasi: Konsultasikan dengan agen atau model yang lebih mumpuni untuk sebagian subtugas yang kompleks.

Konfigurasikan koordinator

Saat mendefinisikan agen Anda, atur multiagent untuk mendeklarasikan daftar (roster) agen yang dapat didelegasikan oleh koordinator:

bash
  coordinator=$(curl -fsS 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" \
    -H "content-type: application/json" \
    -d @- <<EOF
  {
    "name": "Engineering Lead",
    "model": "haijun-opus-5-5",
    "system": "You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.",
    "tools": [
      {
        "type": "agent_toolset_20260401"
      }
    ],
    "multiagent": {
      "type": "coordinator",
      "agents": [
        {"type": "agent", "id": "$REVIEWER_AGENT_ID"},
        {"type": "agent", "id": "$TEST_WRITER_AGENT_ID"}
      ]
    }
  }
  EOF
  )
bash
    ant apply engineering-lead.md reviewer.md test-writer.md
markdown
      ---
      name: Engineering Lead
      model: haijun-opus-5-5
      tools:
        - type: agent_toolset_20260401
      multiagent:
        type: coordinator
        agents: # paths: ant apply substitutes {type: agent, id, version}
          - ./reviewer.md
          - ./test-writer.md
      ---

      You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.
markdown
      ---
      name: reviewer
      model: haijun-haiku-4-5
      ---

      You are a code reviewer.
markdown
      ---
      name: test-writer
      model: haijun-haiku-4-5
      ---

      You write unit tests.
python
  coordinator = client.beta.agents.create(
      name="Engineering Lead",
      model="haijun-opus-5-5",
      system="You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.",
      tools=[
          {"type": "agent_toolset_20260401"},
      ],
      multiagent={
          "type": "coordinator",
          "agents": [
              {"type": "agent", "id": reviewer_agent.id},
              {"type": "agent", "id": test_writer_agent.id},
          ],
      },
  )
typescript
  const coordinator = await client.beta.agents.create({
    name: "Engineering Lead",
    model: "haijun-opus-5-5",
    system:
      "You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.",
    tools: [{ type: "agent_toolset_20260401" }],
    multiagent: {
      type: "coordinator",
      agents: [
        { type: "agent", id: reviewerAgent.id },
        { type: "agent", id: testWriterAgent.id },
      ],
    },
  });
csharp
  var coordinator = await client.Beta.Agents.Create(new()
  {
      Name = "Engineering Lead",
      Model = BetaManagedAgentsModel.HaijunOpus5_5,
      System = "You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.",
      Tools =
      [
          new BetaManagedAgentsAgentToolset20260401Params
          {
              Type = BetaManagedAgentsAgentToolset20260401ParamsType.AgentToolset20260401,
          },
      ],
      Multiagent = new BetaManagedAgentsMultiagentParams
      {
          Type = BetaManagedAgentsMultiagentParamsType.Coordinator,
          Agents = [reviewerAgent.ID, testWriterAgent.ID],
      },
  });
go
  coordinator, err := client.Beta.Agents.New(ctx, juglow.BetaAgentNewParams{
  	Name:   "Engineering Lead",
  	Model:  juglow.BetaManagedAgentsModelConfigParams{ID: juglow.BetaManagedAgentsModelHaijunOpus5_5},
  	System: juglow.String("You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent."),
  	Tools: []juglow.BetaAgentNewParamsToolUnion{{
  		OfAgentToolset20260401: &juglow.BetaManagedAgentsAgentToolset20260401Params{
  			Type: juglow.BetaManagedAgentsAgentToolset20260401ParamsTypeAgentToolset20260401,
  		},
  	}},
  	Multiagent: juglow.BetaManagedAgentsMultiagentParams{
  		Type: juglow.BetaManagedAgentsMultiagentParamsTypeCoordinator,
  		Agents: []juglow.BetaManagedAgentsMultiagentRosterEntryParamsUnion{
  			{OfString: juglow.String(reviewerAgent.ID)},
  			{OfString: juglow.String(testWriterAgent.ID)},
  		},
  	},
  })
  if err != nil {
  	panic(err)
  }
java
  var coordinator = client.beta().agents().create(
      AgentCreateParams.builder()
          .name("Engineering Lead")
          .model(BetaManagedAgentsModel.HAIJUN_OPUS_5_5)
          .system("You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.")
          .addTool(
              BetaManagedAgentsAgentToolset20260401Params.builder()
                  .type(BetaManagedAgentsAgentToolset20260401Params.Type.AGENT_TOOLSET_20260401)
                  .build()
          )
          .multiagent(BetaManagedAgentsMultiagentParams.builder()
              .type(BetaManagedAgentsMultiagentParams.Type.COORDINATOR)
              .addAgent(BetaManagedAgentsAgentParams.builder()
                  .type(BetaManagedAgentsAgentParams.Type.AGENT)
                  .id(reviewerAgent.id())
                  .build())
              .addAgent(BetaManagedAgentsAgentParams.builder()
                  .type(BetaManagedAgentsAgentParams.Type.AGENT)
                  .id(testWriterAgent.id())
                  .build())
              .build())
          .build()
  );
php
  $coordinator = $client->beta->agents->create(
      name: 'Engineering Lead',
      model: 'haijun-opus-5-5',
      system: 'You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.',
      tools: [
          ['type' => 'agent_toolset_20260401'],
      ],
      multiagent: [
          'type' => 'coordinator',
          'agents' => [
              ['type' => 'agent', 'id' => $reviewerAgent->id],
              ['type' => 'agent', 'id' => $testWriterAgent->id],
          ],
      ],
  );
ruby
  coordinator = client.beta.agents.create(
    name: "Engineering Lead",
    model: "haijun-opus-5-5",
    system: "You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.",
    tools: [
      {type: "agent_toolset_20260401"}
    ],
    multiagent: {
      type: "coordinator",
      agents: [
        {type: "agent", id: reviewer_agent.id},
        {type: "agent", id: test_writer_agent.id}
      ]
    }
  )

multiagent.agents dapat menerima salah satu dari berikut ini:

  • {"type": "agent", "id": agent.id} mereferensikan agent yang telah dibuat sebelumnya berdasarkan ID. Jika version tidak ditentukan, referensi disematkan ke versi terbaru agen tersebut pada saat koordinator dibuat.
  • {"type": "agent", "id": agent.id, "version": agent.version} menyematkan versi agen tertentu.
  • {"type": "self"} memungkinkan koordinator membuat salinan dirinya sendiri. Jika sesi dibuat dengan override konfigurasi agen, override tersebut juga berlaku untuk salinan ini; entri roster yang direferensikan berdasarkan ID tidak terpengaruh.
  • {"type": "advisor", "model": ""} memberikan primary thread sesi sebuah advisor yang dapat dikonsultasikan di tengah giliran. Maksimal satu entri advisor per roster. Lihat Berikan sesi sebuah advisor.

Dalam file agen ant apply (tab CLI), entri roster juga dapat berupa path ke file agen lain, seperti ./reviewer.md. Apply akan membuat agen tersebut terlebih dahulu, lalu mengganti path dengan referensi {"type": "agent", "id": ..., "version": ...} yang disematkan.

Konfigurasi koordinator, termasuk roster multiagent.agents-nya, di-snapshot saat koordinator dibuat atau diperbarui. Agen yang direferensikan tetap disematkan ke versi yang diselesaikan pada saat itu dan tidak secara otomatis mengambil pembaruan selanjutnya pada definisinya. Untuk mendelegasikan ke versi yang lebih baru dari agen yang direferensikan, perbarui koordinator agar roster-nya mereferensikan versi tersebut.

Koordinator hanya dapat mendelegasikan ke satu tingkat agen; mereferensikan agen yang memiliki roster multiagent.agents sendiri akan menggagalkan permintaan pembuatan atau pembaruan dengan kesalahan validasi. Maksimal 20 agen unik dapat dicantumkan dalam multiagent.agents, tetapi koordinator dapat memanggil beberapa salinan dari setiap agen.

Ketika agen menyematkan geografi inferensi (model.inference_geo dalam definisi agen), sematan koordinator dan sematan setiap anggota roster harus semuanya diatur ke nilai yang sama atau semuanya tidak diatur. Roster yang tidak cocok ditolak dengan kesalahan validasi 400, baik saat agen disimpan maupun saat override pembuatan sesi mengubah salah satu sematan tersebut.

Berikan sesi sebuah advisor

Entri advisor dalam multiagent.agents memberikan primary thread sesi sebuah advisor (penasihat): model yang dapat dikonsultasikan di tengah giliran untuk panduan strategis, seperti merencanakan pendekatan, keluar dari kebuntuan, atau meninjau pekerjaan sebelum selesai. Entri ini memiliki tepat dua field, type dan model:

bash
curl -fsS 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" \
  -H "content-type: application/json" \
  -d '{
    "name": "Backend engineer",
    "model": "haijun-sonnet-5",
    "system": "You implement backend features end to end. Consult the advisor before major backend design decisions.",
    "multiagent": {
      "type": "coordinator",
      "agents": [
        {"type": "advisor", "model": "haijun-opus-5-5"}
      ]
    }
  }'

Sebuah roster dapat berisi maksimal satu entri advisor, bersama dengan bentuk roster lainnya. Entri ini menempati nama roster yang dicadangkan juglow.advisor: roster yang mencantumkan entri advisor sekaligus anggota yang secara harfiah bernama juglow.advisor ditolak dengan kesalahan validasi 400. Dalam respons, entri advisor ditampilkan terakhir dalam roster terlepas dari posisi saat dikirimkan.

Model advisor harus memenuhi batas kemampuan minimum, dan model agen itu sendiri tidak boleh lebih mumpuni daripada advisor-nya; model dengan kemampuan setara dapat dipasangkan. Pasangan yang tidak valid ditolak dengan kesalahan validasi 400 saat agen disimpan. Pasangan yang valid mengikuti tabel kompatibilitas model alat advisor.

Advisor juga tersedia sebagai alat server pada Messages API. Permukaan Managed Agents berbeda dalam konfigurasi dan pengiriman: entri roster tidak memiliki field max_uses, max_tokens, atau caching, dan saran tiba melalui event thread alih-alih blok advisor_tool_result.

Cara kerja konsultasi

Setiap konsultasi berjalan sebagai thread yang dibuat oleh platform bernama juglow.advisor yang mengakhiri dirinya sendiri saat konsultasi selesai, dan saran dikirimkan ke primary thread sebagai event agent.thread_message_received. Sebuah konsultasi memancarkan event thread standar, yang diidentifikasi dengan nama cadangan juglow.advisor (event siklus hidup thread membawanya sebagai agent_name, dan pengiriman saran membawanya sebagai from_agent_name), biasanya dalam urutan ini:

  1. session.thread_created
  1. session.thread_status_running
  1. agent.thread_message_received (saran)
  1. session.thread_status_idle (stop_reason: end_turn)
  1. session.thread_status_terminated

Tidak ada event agent.tool_use yang dipancarkan untuk konsultasi, dan tidak ada event agent.thread_message_sent yang muncul di aliran event sesi, karena input konsultasi disusun oleh platform alih-alih dikirim oleh agen. Jika Anda mencantumkan event milik thread advisor itu sendiri, saran juga muncul di sana sebagai event agent.thread_message_sent. Pengiriman saran (event 3) tidak dijamin tiba sebelum event idle dan terminated dari thread advisor, jadi jangan perlakukan event tersebut sebagai sinyal bahwa saran sudah dikirimkan.

Apakah klien Anda dapat membaca saran tersebut bergantung pada kebijakan model advisor, dan ini mencerminkan pembagian varian hasil pada alat advisor Messages API. Model advisor yang mengembalikan hasil plaintext di sana mengirimkan saran sebagai konten teks yang dapat dibaca di sini; model advisor yang mengembalikan hasil yang disunting (redacted) di sana mengirimkan placeholder [{"type": "redacted"}] sebagai konten pesan di setiap permukaan klien, sementara agen itu sendiri tetap membaca saran lengkap di sisi server. Dalam contoh sebelumnya, Haijun Opus 5 adalah advisor dengan hasil redacted, sehingga klien Anda melihat placeholder sementara agen membaca saran lengkap; pilih Haijun Opus 4.8 sebagai advisor jika Anda ingin saran dapat dibaca di aliran event. Pemikiran advisor tidak pernah ditampilkan. Klien tidak dapat mengirim blok redacted sendiri; event yang berisi blok tersebut ditolak dengan kesalahan validasi 400.

Konsultasi yang gagal atau terinterupsi tidak pernah menggagalkan giliran agen: agen melanjutkan setelah pemberitahuan umum bahwa konsultasi gagal. user.interrupt tingkat sesi selama konsultasi mengakhiri thread advisor tanpa saran yang dikirimkan; user.interrupt dengan session_thread_id milik thread advisor hanya membatalkan konsultasi tersebut.

Thread advisor

Advisor bukan agen roster: ia tidak terlihat oleh alat list_agents koordinator, tidak dapat dikirimi pesan dengan send_to_agent, dan hanya primary thread sesi yang dapat berkonsultasi dengannya. Agen roster tidak dapat.

Thread advisor dikecualikan dari batas thread bersamaan. Thread ini muncul dalam daftar thread sesi dengan agent diatur ke bentuk advisor persis seperti yang dikonfigurasi ({"type": "advisor", "model": ...}) dan parent_thread_id diatur ke primary thread.

Caching prompt di sisi advisor bersifat otomatis; tidak ada yang perlu dikonfigurasi. Konsultasi ditagih dengan tarif model advisor, dan tokennya muncul dalam penggunaan thread advisor dan dalam total penggunaan sesi.

Menghapus advisor

Untuk menghapus advisor, perbarui agen dengan roster yang tidak lagi menyertakan entri advisor. Jika advisor adalah satu-satunya entri roster, kosongkan roster sepenuhnya dengan mengatur "multiagent": null.

Buat sesi

Buat sesi yang mereferensikan koordinator. Koordinator mendelegasikan ke agen dalam roster-nya sesuai kebutuhan.

bash
  session=$(curl -fsSL 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" \
    -d @- <<EOF
  {
    "agent": "$COORDINATOR_ID",
    "environment_id": "$ENVIRONMENT_ID"
  }
  EOF
  )
  SESSION_ID=$(jq -r '.id' <<< "$session")
bash
  ant beta:sessions create \
    --agent "$COORDINATOR_ID" \
    --environment-id "$ENVIRONMENT_ID"
python
  session = client.beta.sessions.create(
      agent=coordinator.id,
      environment_id=environment.id,
  )
typescript
  const session = await client.beta.sessions.create({
    agent: coordinator.id,
    environment_id: environment.id,
  });
csharp
  var session = await client.Beta.Sessions.Create(new()
  {
      Agent = coordinator.ID,
      EnvironmentID = environment.ID,
  });
go
  session, err := client.Beta.Sessions.New(ctx, juglow.BetaSessionNewParams{
  	Agent: juglow.BetaSessionNewParamsAgentUnion{
  		OfString: juglow.String(coordinator.ID),
  	},
  	EnvironmentID: environment.ID,
  })
  if err != nil {
  	panic(err)
  }
java
  var session = client.beta().sessions().create(SessionCreateParams.builder()
      .agent(coordinator.id())
      .environmentId(environment.id())
      .build());
php
  $session = $client->beta->sessions->create(
      agent: $coordinator->id,
      environmentID: $environment->id,
  );
ruby
  session = client.beta.sessions.create(
    agent: coordinator.id,
    environment_id: environment.id
  )

Hubungkan agen ke server MCP

Server MCP memiliki cakupan agen (setiap definisi agen mendeklarasikan server dan alatnya sendiri), sedangkan kredensial vault memiliki cakupan sesi (vault_ids yang diteruskan saat pembuatan sesi berlaku untuk setiap thread). Dua implikasi untuk integrasi Anda:

  • Untuk mengautentikasi server MCP, sertakan kredensial vault untuk setiap server MCP yang digunakan di semua agen.
  • Untuk membatasi akses agen, deklarasikan hanya server yang dibutuhkannya dalam definisi agennya.

Override konfigurasi agen saat pembuatan sesi dapat menggantikan server MCP koordinator dan server MCP salinan self-nya.

Buat agen researcher, yang mendeklarasikan server MCP GitHub, dan koordinator yang mendelegasikan ke researcher tersebut:

bash
  research_agent_id=$(curl --fail-with-body -sS "$BASE/v1/agents" "${H[@]}" --data @- <<'EOF' | jq -er '.id'
  {
    "name": "researcher",
    "model": "haijun-haiku-4-5",
    "mcp_servers": [{"type": "url", "name": "github", "url": "https://api.githubcopilot.com/mcp/"}],
    "tools": [{"type": "mcp_toolset", "mcp_server_name": "github"}]
  }
  EOF
  )

  coordinator_id=$(curl --fail-with-body -sS "$BASE/v1/agents" "${H[@]}" --data @- <<EOF | jq -er '.id'
  {
    "name": "coordinator",
    "model": "haijun-opus-5-5",
    "tools": [{"type": "agent_toolset_20260401"}],
    "multiagent": {
      "type": "coordinator",
      "agents": [{"type": "agent", "id": "$research_agent_id"}]
    }
  }
  EOF
  )
bash
    ant apply coordinator.md researcher.md
markdown
      ---
      name: coordinator
      model: haijun-opus-5-5
      tools:
        - type: agent_toolset_20260401
      multiagent:
        type: coordinator
        agents: # path: ant apply substitutes {type: agent, id, version}
          - ./researcher.md
      ---
markdown
      ---
      name: researcher
      model: haijun-haiku-4-5
      mcp_servers:
        - type: url
          name: github
          url: https://api.githubcopilot.com/mcp/
      tools:
        - type: mcp_toolset
          mcp_server_name: github
      ---
python
  research_agent = client.beta.agents.create(
      name="researcher",
      model="haijun-haiku-4-5",
      mcp_servers=[
          {"type": "url", "name": "github", "url": "https://api.githubcopilot.com/mcp/"},
      ],
      tools=[{"type": "mcp_toolset", "mcp_server_name": "github"}],
  )

  coordinator = client.beta.agents.create(
      name="coordinator",
      model="haijun-opus-5-5",
      tools=[{"type": "agent_toolset_20260401"}],
      multiagent={
          "type": "coordinator",
          "agents": [{"type": "agent", "id": research_agent.id}],
      },
  )
typescript
  const researchAgent = await client.beta.agents.create({
    name: "researcher",
    model: "haijun-haiku-4-5",
    mcp_servers: [
      { type: "url", name: "github", url: "https://api.githubcopilot.com/mcp/" },
    ],
    tools: [{ type: "mcp_toolset", mcp_server_name: "github" }],
  });

  const coordinator = await client.beta.agents.create({
    name: "coordinator",
    model: "haijun-opus-5-5",
    tools: [{ type: "agent_toolset_20260401" }],
    multiagent: {
      type: "coordinator",
      agents: [{ type: "agent", id: researchAgent.id }],
    },
  });
csharp
  var researchAgent = await client.Beta.Agents.Create(new()
  {
      Name = "researcher",
      Model = BetaManagedAgentsModel.HaijunHaiku4_5,
      McpServers =
      [
          new()
          {
              Type = BetaManagedAgentsUrlMcpServerParamsType.Url,
              Name = "github",
              Url = "https://api.githubcopilot.com/mcp/",
          },
      ],
      Tools =
      [
          new BetaManagedAgentsMcpToolsetParams
          {
              Type = BetaManagedAgentsMcpToolsetParamsType.McpToolset,
              McpServerName = "github",
          },
      ],
  });

  var coordinator = await client.Beta.Agents.Create(new()
  {
      Name = "coordinator",
      Model = BetaManagedAgentsModel.HaijunOpus5_5,
      Tools =
      [
          new BetaManagedAgentsAgentToolset20260401Params
          {
              Type = BetaManagedAgentsAgentToolset20260401ParamsType.AgentToolset20260401,
          },
      ],
      Multiagent = new()
      {
          Type = BetaManagedAgentsMultiagentParamsType.Coordinator,
          Agents =
          [
              new BetaManagedAgentsAgentParams
              {
                  Type = BetaManagedAgentsAgentParamsType.Agent,
                  ID = researchAgent.ID,
              },
          ],
      },
  });
go
  researcher, err := client.Beta.Agents.New(ctx, juglow.BetaAgentNewParams{
  	Name:  "researcher",
  	Model: juglow.BetaManagedAgentsModelConfigParams{ID: juglow.BetaManagedAgentsModelHaijunHaiku4_5},
  	MCPServers: []juglow.BetaManagedAgentsURLMCPServerParams{{
  		Type: juglow.BetaManagedAgentsURLMCPServerParamsTypeURL,
  		Name: "github",
  		URL:  "https://api.githubcopilot.com/mcp/",
  	}},
  	Tools: []juglow.BetaAgentNewParamsToolUnion{{
  		OfMCPToolset: &juglow.BetaManagedAgentsMCPToolsetParams{
  			Type:          juglow.BetaManagedAgentsMCPToolsetParamsTypeMCPToolset,
  			MCPServerName: "github",
  		},
  	}},
  })
  if err != nil {
  	panic(err)
  }

  coordinator, err := client.Beta.Agents.New(ctx, juglow.BetaAgentNewParams{
  	Name:  "coordinator",
  	Model: juglow.BetaManagedAgentsModelConfigParams{ID: juglow.BetaManagedAgentsModelHaijunOpus5_5},
  	Tools: []juglow.BetaAgentNewParamsToolUnion{{
  		OfAgentToolset20260401: &juglow.BetaManagedAgentsAgentToolset20260401Params{
  			Type: juglow.BetaManagedAgentsAgentToolset20260401ParamsTypeAgentToolset20260401,
  		},
  	}},
  	Multiagent: juglow.BetaManagedAgentsMultiagentParams{
  		Type: juglow.BetaManagedAgentsMultiagentParamsTypeCoordinator,
  		Agents: []juglow.BetaManagedAgentsMultiagentRosterEntryParamsUnion{{
  			OfBetaManagedAgentsAgents: &juglow.BetaManagedAgentsAgentParams{
  				Type: juglow.BetaManagedAgentsAgentParamsTypeAgent,
  				ID:   researcher.ID,
  			},
  		}},
  	},
  })
  if err != nil {
  	panic(err)
  }
java
  var researcher = client.beta().agents().create(
      AgentCreateParams.builder()
          .name("researcher")
          .model(BetaManagedAgentsModel.HAIJUN_HAIKU_4_5)
          .addMcpServer(BetaManagedAgentsUrlMcpServerParams.builder()
              .name("github")
              .type(BetaManagedAgentsUrlMcpServerParams.Type.URL)
              .url("https://api.githubcopilot.com/mcp/")
              .build())
          .addTool(BetaManagedAgentsMcpToolsetParams.builder()
              .type(BetaManagedAgentsMcpToolsetParams.Type.MCP_TOOLSET)
              .mcpServerName("github")
              .build())
          .build()
  );

  var coordinator = client.beta().agents().create(
      AgentCreateParams.builder()
          .name("coordinator")
          .model(BetaManagedAgentsModel.HAIJUN_OPUS_5_5)
          .addTool(BetaManagedAgentsAgentToolset20260401Params.builder()
              .type(BetaManagedAgentsAgentToolset20260401Params.Type.AGENT_TOOLSET_20260401)
              .build())
          .multiagent(BetaManagedAgentsMultiagentParams.builder()
              .type(BetaManagedAgentsMultiagentParams.Type.COORDINATOR)
              .addAgent(BetaManagedAgentsAgentParams.builder()
                  .type(BetaManagedAgentsAgentParams.Type.AGENT)
                  .id(researcher.id())
                  .build())
              .build())
          .build()
  );
php
  $researchAgent = $client->beta->agents->create(
      name: 'researcher',
      model: 'haijun-haiku-4-5',
      mcpServers: [
          ['type' => 'url', 'name' => 'github', 'url' => 'https://api.githubcopilot.com/mcp/'],
      ],
      tools: [
          ['type' => 'mcp_toolset', 'mcp_server_name' => 'github'],
      ],
  );

  $coordinator = $client->beta->agents->create(
      name: 'coordinator',
      model: 'haijun-opus-5-5',
      tools: [
          ['type' => 'agent_toolset_20260401'],
      ],
      multiagent: [
          'type' => 'coordinator',
          'agents' => [
              ['type' => 'agent', 'id' => $researchAgent->id],
          ],
      ],
  );
ruby
  research_agent = client.beta.agents.create(
    name: "researcher",
    model: "haijun-haiku-4-5",
    mcp_servers: [
      {type: "url", name: "github", url: "https://api.githubcopilot.com/mcp/"}
    ],
    tools: [
      {type: "mcp_toolset", mcp_server_name: "github"}
    ]
  )

  coordinator = client.beta.agents.create(
    name: "coordinator",
    model: "haijun-opus-5-5",
    tools: [
      {type: "agent_toolset_20260401"}
    ],
    multiagent: {
      type: "coordinator",
      agents: [
        {type: "agent", id: research_agent.id}
      ]
    }
  )

Kemudian buat sesi dengan vault yang menyimpan kredensial GitHub:

bash
  session_id=$(curl --fail-with-body -sS "$BASE/v1/sessions" "${H[@]}" --data @- <<EOF | jq -er '.id'
  {
    "agent": "$coordinator_id",
    "environment_id": "$environment_id",
    "vault_ids": ["$vault_id"]
  }
  EOF
  )
  echo "$session_id"
bash
  session_id=$(ant beta:sessions create \
    --agent "$coordinator_id" \
    --environment-id "$environment_id" \
    --vault-id "$vault_id" \
    --transform id --raw-output)
  echo "$session_id"
python
  session = client.beta.sessions.create(
      agent=coordinator.id,
      environment_id=environment.id,
      vault_ids=[vault.id],
  )
  print(session.id)
typescript
  const session = await client.beta.sessions.create({
    agent: coordinator.id,
    environment_id: environment.id,
    vault_ids: [vault.id],
  });
  console.log(session.id);
csharp
  var session = await client.Beta.Sessions.Create(new()
  {
      Agent = coordinator.ID,
      EnvironmentID = environment.ID,
      VaultIds = [vault.ID],
  });
  Console.WriteLine(session.ID);
go
  session, err := client.Beta.Sessions.New(ctx, juglow.BetaSessionNewParams{
  	Agent: juglow.BetaSessionNewParamsAgentUnion{
  		OfString: juglow.String(coordinator.ID),
  	},
  	EnvironmentID: environment.ID,
  	VaultIDs:      []string{vault.ID},
  })
  if err != nil {
  	panic(err)
  }
  fmt.Println(session.ID)
java
  var session = client.beta().sessions().create(SessionCreateParams.builder()
      .agent(coordinator.id())
      .environmentId(environment.id())
      .vaultIds(List.of(vault.id()))
      .build());
  IO.println(session.id());
php
  $session = $client->beta->sessions->create(
      agent: $coordinator->id,
      environmentID: $environment->id,
      vaultIDs: [$vault->id],
  );
  echo "{$session->id}\n";
ruby
  session = client.beta.sessions.create(
    agent: coordinator.id,
    environment_id: environment.id,
    vault_ids: [vault.id]
  )
  puts session.id

Dalam contoh ini, hanya researcher yang mendeklarasikan server MCP GitHub, sehingga koordinator tidak memiliki akses. vault_ids sesi menyediakan kredensial GitHub ke thread researcher.

Tip: Jika panggilan MCP agen gagal diautentikasi setelah Anda mendeklarasikan server, pastikan mcp_server_url kredensial merujuk ke server yang sama dengan mcp_servers[].url agen. Kedua URL dinormalisasi sebelum dicocokkan (skema dan host dijadikan huruf kecil, port default dan garis miring di akhir dihapus), sehingga perbedaan huruf besar-kecil pada host, port default, atau garis miring di akhir tidak mencegah kecocokan; path, subdomain, atau port non-default yang berbeda akan mencegahnya.

Thread

Aliran event tingkat sesi (/v1/sessions/{session_id}/events/stream) dianggap sebagai primary thread, yang berisi tampilan ringkas dari semua aktivitas di semua thread. Anda tidak melihat aktivitas lengkap dari subagen, tetapi Anda melihat awal dan akhir pekerjaan mereka, serta event yang memblokir seperti permintaan izin alat.

Session thread adalah tempat Anda menelusuri aktivitas agen tertentu.

status sesi adalah agregasi dari semua aktivitas agen; jika setidaknya satu thread berstatus running, maka status sesi keseluruhan juga running.

Anggaran sesi adalah satu batas bersama untuk semua thread dalam sesi. Saat batas tercapai, thread berhenti sementara secara independen, dan biaya setiap thread dihitung berdasarkan model yang dilayani oleh thread itu sendiri.

Note: Maksimal 25 thread bersamaan didukung. Koordinator dapat memanggil beberapa salinan dari satu agen dalam roster, sehingga membuat beberapa thread yang terkait dengan satu agent. Thread konsultasi advisor dikecualikan dari batas ini.

Daftar thread

Cantumkan semua thread yang terkait dengan sesi sebagai berikut:

bash
  curl -fsS "https://haijun.my.id/v1/sessions/$SESSION_ID/threads" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: managed-agents-2026-04-01" \
    | jq -r '.data[] | "[\(.agent.name)] \(.status)"'
bash
  ant beta:sessions:threads list --session-id "$SESSION_ID"
python
  for thread in client.beta.sessions.threads.list(session.id):
      print(f"[{thread.agent.name}] {thread.status}")
typescript
  for await (const thread of client.beta.sessions.threads.list(session.id)) {
    const name = thread.agent.type === "agent" ? thread.agent.name : "advisor";
    console.log(`[${name}] ${thread.status}`);
  }
csharp
  await foreach (var thread in (await client.Beta.Sessions.Threads.List(session.ID)).Paginate())
  {
      Console.WriteLine($"[{thread.Agent.Name}] {thread.Status}");
  }
go
  threads := client.Beta.Sessions.Threads.ListAutoPaging(ctx, session.ID, juglow.BetaSessionThreadListParams{})
  for threads.Next() {
  	thread := threads.Current()
  	fmt.Printf("[%s] %s\n", thread.Agent.Name, thread.Status)
  }
  if err := threads.Err(); err != nil {
  	panic(err)
  }
java
  for (var thread : client.beta().sessions().threads().list(session.id()).autoPager()) {
      var name = thread.agent().isAgent() ? thread.agent().asAgent().name() : "advisor";
      IO.println("[" + name + "] " + thread.status());
  }
php
  foreach ($client->beta->sessions->threads->list($session->id)->pagingEachItem() as $thread) {
      echo "[{$thread->agent->name}] {$thread->status}\n";
  }
ruby
  client.beta.sessions.threads.list(session.id).auto_paging_each do |thread|
    puts "[#{thread.agent.name}] #{thread.status}"
  end

Daftar lengkap mencakup primary thread. parent_thread_id bernilai null untuk primary thread.

Interupsi session thread

Kirim user.interrupt dengan session_thread_id untuk menghentikan thread tertentu. Menghilangkan session_thread_id akan menginterupsi setiap thread yang tidak diarsipkan dalam sesi, termasuk primary thread.

bash
  curl -fsS "https://haijun.my.id/v1/sessions/$SESSION_ID/events?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 "{\"events\": [{\"type\": \"user.interrupt\", \"session_thread_id\": \"$THREAD_ID\"}]}"
bash
  ant beta:sessions:events send \
    --session-id "$SESSION_ID" \
    --event "{type: user.interrupt, session_thread_id: $THREAD_ID}"
python
  client.beta.sessions.events.send(
      session.id,
      events=[{"type": "user.interrupt", "session_thread_id": thread.id}],
  )
typescript
  await client.beta.sessions.events.send(session.id, {
    events: [{ type: "user.interrupt", session_thread_id: thread.id }],
  });
csharp
  await client.Beta.Sessions.Events.Send(session.ID, new()
  {
      Events =
      [
          new BetaManagedAgentsUserInterruptEventParams
          {
              Type = BetaManagedAgentsUserInterruptEventParamsType.UserInterrupt,
              SessionThreadID = thread.ID,
          },
      ],
  });
go
  if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, juglow.BetaSessionEventSendParams{
  	Events: []juglow.BetaManagedAgentsEventParamsUnion{{
  		OfUserInterrupt: &juglow.BetaManagedAgentsUserInterruptEventParams{
  			Type:            juglow.BetaManagedAgentsUserInterruptEventParamsTypeUserInterrupt,
  			SessionThreadID: juglow.String(thread.ID),
  		},
  	}},
  }); err != nil {
  	panic(err)
  }
java
  client.beta().sessions().events().send(
      session.id(),
      EventSendParams.builder()
          .addEvent(BetaManagedAgentsUserInterruptEventParams.builder()
              .type(BetaManagedAgentsUserInterruptEventParams.Type.USER_INTERRUPT)
              .sessionThreadId(thread.id())
              .build())
          .build());
php
  $client->beta->sessions->events->send(
      $session->id,
      events: [
          ['type' => 'user.interrupt', 'session_thread_id' => $thread->id],
      ],
  );
ruby
  client.beta.sessions.events.send_(
    session.id,
    events: [{type: "user.interrupt", session_thread_id: thread.id}]
  )

Terhadap thread anak yang terblokir pada requires_action, interupsi menutup setiap panggilan alat yang tertunda dengan hasil alat berupa kesalahan ("Tool execution was interrupted before completion. Please retry.") dan memancarkan ulang session.thread_status_idle dengan stop_reason: end_turn secara langsung; model tidak di-sampling. Terhadap thread yang sudah berstatus idle, interupsi tidak melakukan apa pun (no-op).

Arsipkan session thread

Secara opsional, arsipkan session thread ketika telah menyelesaikan pekerjaannya. Ini membebaskan satu thread dari batas 25 thread.

bash
  curl -fsS -X POST "https://haijun.my.id/v1/sessions/$SESSION_ID/threads/$THREAD_ID/archive" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: managed-agents-2026-04-01"
bash
  ant beta:sessions:threads archive \
    --session-id "$SESSION_ID" \
    --thread-id "$THREAD_ID"
python
  archived = client.beta.sessions.threads.archive(thread.id, session_id=session.id)
  print(archived.status, archived.archived_at)
typescript
  const archived = await client.beta.sessions.threads.archive(thread.id, {
    session_id: session.id,
  });
  console.log(archived.status, archived.archived_at);
csharp
  var archived = await client.Beta.Sessions.Threads.Archive(thread.ID, new() { SessionID = session.ID });
  Console.WriteLine($"{archived.Status} {archived.ArchivedAt}");
go
  archived, err := client.Beta.Sessions.Threads.Archive(ctx, thread.ID, juglow.BetaSessionThreadArchiveParams{
  	SessionID: session.ID,
  })
  if err != nil {
  	panic(err)
  }
  fmt.Println(archived.Status, archived.ArchivedAt)
java
  var archived = client.beta().sessions().threads().archive(
      thread.id(),
      ThreadArchiveParams.builder()
          .sessionId(session.id())
          .build());
  IO.println(archived.status() + " " + archived.archivedAt().orElseThrow());
php
  $archived = $client->beta->sessions->threads->archive($thread->id, sessionID: $session->id);
  echo "{$archived->status} {$archived->archivedAt->format(DATE_ATOM)}\n";
ruby
  archived = client.beta.sessions.threads.archive(thread.id, session_id: session.id)
  puts "#{archived.status} #{archived.archived_at}"

Pengarsipan hanya berhasil jika thread berstatus idle. Thread yang terparkir pada requires_action dihitung sebagai idle dan dapat diarsipkan secara langsung; hanya thread yang sedang berjalan yang harus diinterupsi terlebih dahulu:

bash
  # Interupsi thread, lalu arsipkan
  curl -fsS "https://haijun.my.id/v1/sessions/$SESSION_ID/events?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 "{\"events\": [{\"type\": \"user.interrupt\", \"session_thread_id\": \"$THREAD_ID\"}]}"

  curl -fsS -X POST "https://haijun.my.id/v1/sessions/$SESSION_ID/threads/$THREAD_ID/archive" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: managed-agents-2026-04-01"
bash
  ant beta:sessions:events send \
    --session-id "$SESSION_ID" \
    --event "{type: user.interrupt, session_thread_id: $THREAD_ID}"

  ant beta:sessions:threads archive \
    --session-id "$SESSION_ID" \
    --thread-id "$THREAD_ID"
python
  client.beta.sessions.events.send(
      session.id,
      events=[{"type": "user.interrupt", "session_thread_id": thread.id}],
  )
  archived = client.beta.sessions.threads.archive(thread.id, session_id=session.id)
  print(archived.status, archived.archived_at)
typescript
  await client.beta.sessions.events.send(session.id, {
    events: [{ type: "user.interrupt", session_thread_id: thread.id }],
  });
  const archived = await client.beta.sessions.threads.archive(thread.id, {
    session_id: session.id,
  });
  console.log(archived.status, archived.archived_at);
csharp
  await client.Beta.Sessions.Events.Send(session.ID, new()
  {
      Events =
      [
          new BetaManagedAgentsUserInterruptEventParams
          {
              Type = BetaManagedAgentsUserInterruptEventParamsType.UserInterrupt,
              SessionThreadID = thread.ID,
          },
      ],
  });
  archived = await client.Beta.Sessions.Threads.Archive(thread.ID, new() { SessionID = session.ID });
  Console.WriteLine($"{archived.Status} {archived.ArchivedAt}");
go
  if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, juglow.BetaSessionEventSendParams{
  	Events: []juglow.BetaManagedAgentsEventParamsUnion{{
  		OfUserInterrupt: &juglow.BetaManagedAgentsUserInterruptEventParams{
  			Type:            juglow.BetaManagedAgentsUserInterruptEventParamsTypeUserInterrupt,
  			SessionThreadID: juglow.String(thread.ID),
  		},
  	}},
  }); err != nil {
  	panic(err)
  }

  archived, err := client.Beta.Sessions.Threads.Archive(ctx, thread.ID, juglow.BetaSessionThreadArchiveParams{
  	SessionID: session.ID,
  })
  if err != nil {
  	panic(err)
  }
  fmt.Println(archived.Status, archived.ArchivedAt)
java
  client.beta().sessions().events().send(
      session.id(),
      EventSendParams.builder()
          .addEvent(BetaManagedAgentsUserInterruptEventParams.builder()
              .type(BetaManagedAgentsUserInterruptEventParams.Type.USER_INTERRUPT)
              .sessionThreadId(thread.id())
              .build())
          .build());

  archived = client.beta().sessions().threads().archive(
      thread.id(),
      ThreadArchiveParams.builder()
          .sessionId(session.id())
          .build());
  IO.println(archived.status() + " " + archived.archivedAt().orElseThrow());
php
  $client->beta->sessions->events->send(
      $session->id,
      events: [['type' => 'user.interrupt', 'session_thread_id' => $thread->id]],
  );
  $archived = $client->beta->sessions->threads->archive($thread->id, sessionID: $session->id);
  echo "{$archived->status} {$archived->archivedAt->format(DATE_ATOM)}\n";
ruby
  client.beta.sessions.events.send_(
    session.id,
    events: [{type: "user.interrupt", session_thread_id: thread.id}]
  )
  archived = client.beta.sessions.threads.archive(thread.id, session_id: session.id)
  puts "#{archived.status} #{archived.archived_at}"

Event primary thread

Event-event ini menampilkan aktivitas multiagen pada primary thread di /v1/sessions/{session_id}/events/stream. Event arah pesan dinamai relatif terhadap thread tempat event tersebut muncul: agent.thread_message_received berarti sebuah pesan tiba di thread ini dari thread lain, dan agent.thread_message_sent berarti thread ini mengirim pesan. Tugas yang didelegasikan koordinator, misalnya, tiba di aliran milik thread anak sebagai event agent.thread_message_received.

TipeDeskripsi
session.thread_createdSebuah thread dibuat. Mencakup session_thread_id dan agent_name.
session.thread_status_runningSebuah thread memulai aktivitas.
session.thread_status_idleAgen yang terkait dengan thread sedang menunggu input. Mencakup stop_reason yang menunjukkan mengapa agen berhenti.
session.thread_status_terminatedSebuah thread diarsipkan atau mengalami kesalahan terminal.
agent.thread_message_receivedPada primary thread, sebuah agen mengirim laporan atau pertanyaan ke koordinator. Mencakup from_session_thread_id, from_agent_name, dan content.
agent.thread_message_sentPada primary thread, koordinator mengirim tugas atau pesan tindak lanjut ke agen lain. Mencakup to_session_thread_id, to_agent_name, dan content.

Konsultasi advisor memancarkan event thread yang sama ini dengan nama cadangan juglow.advisor (sebagai agent_name pada event siklus hidup thread dan from_agent_name pada pengiriman saran); lihat Berikan sesi sebuah advisor untuk urutannya.

Event session thread

Event penting diproksikan ke primary thread. Namun, Anda mungkin masih ingin menyelidiki penalaran dan panggilan alat dari agen tertentu. Untuk melakukannya, lakukan streaming atau cantumkan event dari session thread yang terkait.

Setiap session thread memiliki aliran event sendiri di /v1/sessions/{session_id}/threads/{thread_id}/stream, dan menerima parameter event_deltas[] yang sama dengan aliran tingkat sesi, sehingga Anda dapat melihat pratinjau teks subagen saat model menghasilkannya. Sebuah koneksi hanya mempratinjau thread yang sedang dibacanya: pratinjau thread anak tidak pernah muncul di aliran tingkat sesi, jadi untuk memantau subagen secara langsung, buka aliran thread miliknya sendiri. Lihat Pratinjau event session thread untuk cara mengaktifkan, mengakumulasi, dan merekonsiliasi pratinjau.

Streaming event thread sesi

bash
  curl -fsSN "https://haijun.my.id/v1/sessions/$SESSION_ID/threads/$THREAD_ID/stream?beta=true" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: managed-agents-2026-04-01" |
    while IFS= read -r line; do
      [[ $line == data:* ]] || continue
      json=${line#data: }
      case $(jq -r '.type' <<<"$json") in
        agent.message)
          printf '%s' "$(jq -j '.content[] | select(.type == "text") | .text' <<<"$json")"
          ;;
        session.thread_status_idle)
          break
          ;;
      esac
    done
bash
  ant beta:sessions:threads:events stream \
    --session-id "$SESSION_ID" \
    --thread-id "$THREAD_ID"
python
  with client.beta.sessions.threads.events.stream(
      thread.id,
      session_id=session.id,
  ) as stream:
      for event in stream:
          match event.type:
              case "agent.message":
                  for block in event.content:
                      if block.type == "text":
                          print(block.text, end="")
              case "session.thread_status_idle":
                  break
typescript
  const stream = await client.beta.sessions.threads.events.stream(thread.id, {
    session_id: session.id,
  });

  loop: for await (const event of stream) {
    switch (event.type) {
      case "agent.message":
        for (const block of event.content) {
          if (block.type === "text") {
            process.stdout.write(block.text);
          }
        }
        break;
      case "session.thread_status_idle":
        break loop;
    }
  }
csharp
  await foreach (var evt in client.Beta.Sessions.Threads.Events.StreamStreaming(thread.ID, new() { SessionID = session.ID }))
  {
      if (evt.Value is BetaManagedAgentsAgentMessageEvent message)
      {
          foreach (var block in message.Content)
          {
              if (block.Type == "text")
              {
                  Console.Write(block.Text);
              }
          }
      }
      else if (evt.Value is BetaManagedAgentsSessionThreadStatusIdleEvent)
      {
          break;
      }
  }
go
  	stream := client.Beta.Sessions.Threads.Events.StreamEvents(ctx, thread.ID, juglow.BetaSessionThreadEventStreamParams{
  		SessionID: session.ID,
  	})
  	defer stream.Close()

  loop:
  	for stream.Next() {
  		event := stream.Current()
  		switch event.Type {
  		case "agent.message":
  			for _, block := range event.AsAgentMessage().Content {
  				if block.Type == "text" {
  					fmt.Print(block.Text)
  				}
  			}
  		case "session.thread_status_idle":
  			break loop
  		}
  	}
  	if err := stream.Err(); err != nil {
  		panic(err)
  	}
java
  try (var streamResponse = client.beta().sessions().threads().events().streamStreaming(
      thread.id(),
      EventStreamParams.builder().sessionId(session.id()).build()
  )) {
      loop:
      for (var event : (Iterable<BetaManagedAgentsStreamSessionThreadEvents>) streamResponse.stream()::iterator) {
          switch (event.type().value()) {
              case AGENT_MESSAGE -> {
                  for (var block : event.asAgentMessage().content()) {
                      block.text().ifPresent(textBlock -> IO.print(textBlock.text()));
                  }
              }
              case SESSION_THREAD_STATUS_IDLE -> {
                  break loop;
              }
          }
      }
  }
php
  $stream = $client->beta->sessions->threads->events->streamStream(
      $thread->id,
      sessionID: $session->id,
  );

  foreach ($stream as $event) {
      switch (true) {
          case $event instanceof \Juglow\Beta\Sessions\Events\ManagedAgentsAgentMessageEvent:
              foreach ($event->content as $block) {
                  if ($block instanceof \Juglow\Beta\Sessions\Events\ManagedAgentsTextBlock) {
                      echo $block->text;
                  }
              }
              break;
          case $event instanceof \Juglow\Beta\Sessions\Events\ManagedAgentsSessionThreadStatusIdleEvent:
              break 2;
      }
  }
ruby
  client.beta.sessions.threads.events.stream_events(thread.id, session_id: session.id).each do |event|
    case event
    when Juglow::Beta::Sessions::BetaManagedAgentsAgentMessageEvent
      event.content.each do |block|
        print block.text if block.is_a?(Juglow::Beta::Sessions::BetaManagedAgentsTextBlock)
      end
    when Juglow::Beta::Sessions::BetaManagedAgentsSessionThreadStatusIdleEvent
      break
    end
  end

Daftar event session thread

Cantumkan semua event session thread yang telah lalu untuk mengambil riwayat lengkap.

bash
  curl -fsS "https://haijun.my.id/v1/sessions/$SESSION_ID/threads/$THREAD_ID/events" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: managed-agents-2026-04-01" \
    | jq -r '.data[] | "[\(.type)] \(.processed_at)"'
bash
  ant beta:sessions:threads:events list \
    --session-id "$SESSION_ID" \
    --thread-id "$THREAD_ID"
python
  for event in client.beta.sessions.threads.events.list(
      thread.id,
      session_id=session.id,
  ):
      print(f"[{event.type}] {event.processed_at}")
typescript
  for await (const event of client.beta.sessions.threads.events.list(thread.id, {
    session_id: session.id,
  })) {
    console.log(`[${event.type}] ${event.processed_at}`);
  }
csharp
  var page = await client.Beta.Sessions.Threads.Events.List(thread.ID, new() { SessionID = session.ID });
  await foreach (var evt in page.Paginate())
  {
      Console.WriteLine($"[{evt.Type}] {evt.ProcessedAt}");
  }
go
  pager := client.Beta.Sessions.Threads.Events.ListAutoPaging(ctx, thread.ID, juglow.BetaSessionThreadEventListParams{
  	SessionID: session.ID,
  })
  for pager.Next() {
  	event := pager.Current()
  	fmt.Printf("[%s] %s\n", event.Type, event.ProcessedAt)
  }
  if err := pager.Err(); err != nil {
  	panic(err)
  }
java
  for (var event : client.beta().sessions().threads().events().list(
          thread.id(),
          EventListParams.builder().sessionId(session.id()).build()
      ).autoPager()) {
      var type = event._json().orElseThrow() instanceof JsonObject json
          ? json.values().get("type").asStringOrThrow()
          : "unknown";
      var processedAt = event.processedAt().map(OffsetDateTime::toString).orElse("pending");
      IO.println("[" + type + "] " + processedAt);
  }
php
  foreach (
      $client->beta->sessions->threads->events->list(
          $thread->id,
          sessionID: $session->id,
      )->pagingEachItem() as $event
  ) {
      echo "[{$event->type}] {$event->processedAt->format(DATE_RFC3339)}\n";
  }
ruby
  client.beta.sessions.threads.events.list(
    thread.id,
    session_id: session.id
  ).auto_paging_each do |event|
    puts "[#{event.type}] #{event.processed_at}"
  end

Izin alat dan alat kustom

Jika subagen membutuhkan sesuatu dari klien Anda, seperti izin untuk menjalankan panggilan alat atau hasil alat kustom, event tersebut juga diposting ke thread utama dengan session_thread_id yang mengidentifikasi thread sesi asalnya. Panggilan alat memerlukan izin Anda jika menggunakan always_ask, atau jika menggunakan auto dan server tidak dapat mengambil keputusan.

json
{
  "type": "session.thread_status_idle",
  "id": "sevt_01ABC...",
  "session_thread_id": "sth_01DEF...",
  "agent_name": "code-reviewer",
  "stop_reason": {
    "type": "requires_action",
    "event_ids": ["sevt_01XYZ..."]
  }
}

Posting user.tool_confirmation (dengan tool_use_id) atau user.custom_tool_result (dengan custom_tool_use_id); server merutekan respons ke thread yang benar secara otomatis.

Dengan auto, event user.message Anda dapat membuat server mengizinkan panggilan yang seharusnya ditolak. Namun, tidak ada apa pun di thread subagen yang dianggap sebagai maksud Anda: klien Anda tidak memposting pesan apa pun di sana, dan pesan koordinator kepada subagen tidak diperhitungkan. Ketika server menolak panggilan dengan auto, tidak ada yang diposting ke thread utama. Event dan hasil alat berupa error hanya muncul di aliran thread milik subagen itu sendiri, dan subagen tetap berjalan.

Contoh berikut memperluas handler konfirmasi alat untuk merutekan balasan. Pola yang sama berlaku untuk user.custom_tool_result.

bash
  while IFS= read -r event_id; do
    jq -n --arg id "$event_id" \
      '{events: [{type: "user.tool_confirmation", tool_use_id: $id, result: "allow"}]}' |
      curl -fsS "https://haijun.my.id/v1/sessions/$SESSION_ID/events?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 @-
  done < <(jq -r '.stop_reason.event_ids[]' <<<"$data")
bash
  # Alur kerja ini tidak cocok diterjemahkan menjadi perintah shell sekali jalan.
  # Sebagai gantinya, gunakan salah satu contoh SDK di grup kode ini.
python
  for event_id in stop.event_ids:
      client.beta.sessions.events.send(
          session.id,
          events=[
              {
                  "type": "user.tool_confirmation",
                  "tool_use_id": event_id,
                  "result": "allow",
              }
          ],
      )
typescript
  for (const eventId of stop.event_ids) {
    await client.beta.sessions.events.send(session.id, {
      events: [
        {
          type: "user.tool_confirmation",
          tool_use_id: eventId,
          result: "allow",
        },
      ],
    });
  }
csharp
  foreach (var eventId in requiresAction.EventIds)
  {
      await client.Beta.Sessions.Events.Send(session.ID, new()
      {
          Events =
          [
              new BetaManagedAgentsUserToolConfirmationEventParams
              {
                  Type = BetaManagedAgentsUserToolConfirmationEventParamsType.UserToolConfirmation,
                  ToolUseID = eventId,
                  Result = BetaManagedAgentsUserToolConfirmationEventParamsResult.Allow,
              },
          ],
      });
  }
go
  for _, eventID := range stopReason.EventIDs {
  	params := juglow.BetaManagedAgentsUserToolConfirmationEventParams{
  		Type:      juglow.BetaManagedAgentsUserToolConfirmationEventParamsTypeUserToolConfirmation,
  		ToolUseID: eventID,
  		Result:    juglow.BetaManagedAgentsUserToolConfirmationEventParamsResultAllow,
  	}
  	if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, juglow.BetaSessionEventSendParams{
  		Events: []juglow.BetaManagedAgentsEventParamsUnion{{OfUserToolConfirmation: &params}},
  	}); err != nil {
  		panic(err)
  	}
  }
java
  for (var eventId : pendingToolUseIds) {
      client.beta().sessions().events().send(
          session.id(),
          EventSendParams.builder()
              .addEvent(BetaManagedAgentsUserToolConfirmationEventParams.builder()
                  .toolUseId(eventId)
                  .result(BetaManagedAgentsUserToolConfirmationEventParams.Result.ALLOW)
                  .build())
              .build()
      );
  }
php
  foreach ($event->stopReason->eventIDs as $eventId) {
      $client->beta->sessions->events->send($session->id, events: [[
          'type' => 'user.tool_confirmation',
          'tool_use_id' => $eventId,
          'result' => 'allow',
      ]]);
  }
ruby
  event_ids.each do |event_id|
    client.beta.sessions.events.send_(session.id, events: [{
      type: "user.tool_confirmation",
      tool_use_id: event_id,
      result: "allow"
    }])
  end
On this page
Cara kerjanyaApa yang perlu didelegasikanKonfigurasikan koordinatorBerikan sesi sebuah advisorCara kerja konsultasiThread advisorMenghapus advisorBuat sesiHubungkan agen ke server MCPThreadEvent primary threadEvent session threadIzin alat dan alat kustom