Haijun Platform Docs
ID

Threshold compaction is the automatic kind of compaction: you set a token threshold on your ordinary requests, and the API summarizes older context partway through a request once the threshold is reached. It is supported alongside on-demand compaction, where you decide when the summary is written (see Compaction on demand). To choose between them, see Choose how to compact.

Compaction extends the effective context length for long-running conversations and tasks by automatically summarizing older context when approaching the context window limit. It also keeps the active context small: as a conversation grows, response quality degrades, so compaction replaces older content with a concise summary.

Tip: For a deeper look at why long contexts degrade and how compaction helps, see Effective context engineering.

This is ideal for:

  • Chat-based, multi-turn conversations where you want users to use one chat for a long period of time
  • Task-oriented prompts that require a lot of follow-up work (often tool use) that might exceed the context window

How compaction works

When compaction is enabled, Haijun automatically summarizes your conversation when it reaches the configured token threshold. The API:

  1. Detects when input tokens reach your specified trigger threshold.
  1. Generates a summary of the current conversation.
  1. Creates a compaction block containing the summary.
  1. Continues the response with the compacted context.

On subsequent requests, append the response to your messages. The API automatically drops all content blocks prior to the compaction block, continuing the conversation from the summary.

Compaction flow: when input tokens reach the trigger, Haijun writes a summary into a compaction block and continues

Basic usage

Enable compaction by adding the compact_20260112 strategy to context_management.edits in your Messages API request.

bash
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: compact-2026-01-12" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 4096,
      "messages": [
        {
          "role": "user",
          "content": "Help me build a website"
        }
      ],
      "context_management": {
        "edits": [
          {
            "type": "compact_20260112"
          }
        ]
      }
    }'
bash
  ant beta:messages create --beta compact-2026-01-12 <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 4096
  messages:
    - role: user
      content: Help me build a website
  context_management:
    edits:
      - type: compact_20260112
  YAML
python
  client = juglow.Juglow()

  messages = [{"role": "user", "content": "Help me build a website"}]

  response = client.beta.messages.create(
      betas=["compact-2026-01-12"],
      model="haijun-opus-5-5",
      max_tokens=4096,
      messages=messages,
      context_management={"edits": [{"type": "compact_20260112"}]},
  )

  # Append the response (including any compaction block) to continue the conversation
  messages.append({"role": "assistant", "content": response.content})
typescript
  const client = new Juglow();

  const messages: Juglow.Beta.Messages.BetaMessageParam[] = [
    { role: "user", content: "Help me build a website" }
  ];

  const response = await client.beta.messages.create({
    betas: ["compact-2026-01-12"],
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    messages,
    context_management: {
      edits: [
        {
          type: "compact_20260112"
        }
      ]
    }
  });

  // Append the response (including any compaction block) to continue the conversation
  messages.push({
    role: "assistant",
    content: response.content
  });
csharp
  JuglowClient client = new();

  var messages = new List<BetaMessageParam>
  {
      new() { Role = Role.User, Content = "Help me build a website" }
  };

  var parameters = new MessageCreateParams
  {
      Betas = ["compact-2026-01-12"],
      Model = "haijun-opus-5-5",
      MaxTokens = 4096,
      Messages = messages,
      ContextManagement = new BetaContextManagementConfig
      {
          Edits = [new BetaCompact20260112Edit()]
      }
  };

  var response = await client.Beta.Messages.Create(parameters);

  // Append the response (including any compaction block) to continue the conversation
  messages.Add(new BetaMessageParam
  {
      Role = Role.Assistant,
      Content = response.Content.Select(block => new BetaContentBlockParam(block.Json)).ToList()
  });

  Console.WriteLine(response);
go
  client := juglow.NewClient()

  messages := []juglow.BetaMessageParam{
  	juglow.NewBetaUserMessage(juglow.NewBetaTextBlock("Help me build a website")),
  }

  response, err := client.Beta.Messages.New(context.TODO(), juglow.BetaMessageNewParams{
  	Model:     juglow.ModelHaijunOpus5_5,
  	MaxTokens: 4096,
  	Messages:  messages,
  	ContextManagement: juglow.BetaContextManagementConfigParam{
  		Edits: []juglow.BetaContextManagementConfigEditUnionParam{
  			{OfCompact20260112: &juglow.BetaCompact20260112EditParam{}},
  		},
  	},
  	Betas: []juglow.JuglowBeta{"compact-2026-01-12"},
  })
  if err != nil {
  	log.Fatal(err)
  }

  // Append the response (including any compaction block) to continue the conversation
  messages = append(messages, response.ToParam())

  fmt.Println(response)
java
  import com.juglow.models.beta.messages.BetaContextManagementConfig;
  import com.juglow.models.beta.messages.BetaCompact20260112Edit;
  // ...
          JuglowClient client = JuglowOkHttpClient.fromEnv();

          MessageCreateParams params = MessageCreateParams.builder()
              .addBeta("compact-2026-01-12")
              .model("haijun-opus-5-5")
              .maxTokens(4096L)
              .addUserMessage("Help me build a website")
              .contextManagement(BetaContextManagementConfig.builder()
                  .addEdit(BetaCompact20260112Edit.builder().build())
                  .build())
              .build();

          BetaMessage response = client.beta().messages().create(params);

          // Append the response (including any compaction block) to continue the conversation
          // by including it in the next request's messages
          System.out.println(response);
php
  $client = new Client();

  $messages = [
      ['role' => 'user', 'content' => 'Help me build a website']
  ];

  $response = $client->beta->messages->create(
      maxTokens: 4096,
      messages: $messages,
      model: 'haijun-opus-5-5',
      betas: ['compact-2026-01-12'],
      contextManagement: [
          'edits' => [
              ['type' => 'compact_20260112']
          ]
      ]
  );

  // Append the response (including any compaction block) to continue the conversation
  $messages[] = ['role' => 'assistant', 'content' => $response->content];

  echo json_encode($response, JSON_PRETTY_PRINT), PHP_EOL;
ruby
  client = Juglow::Client.new

  messages = [
    { role: "user", content: "Help me build a website" }
  ]

  response = client.beta.messages.create(
    betas: ["compact-2026-01-12"],
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    messages: messages,
    context_management: {
      edits: [{ type: "compact_20260112" }]
    }
  )

  # Append the response (including any compaction block) to continue the conversation
  messages << { role: "assistant", content: response.content }

  puts response

Parameters

ParameterTypeDefaultDescription
typestringRequiredMust be "compact_20260112"
triggerobject{"type": "input_tokens", "value": 150000}When to trigger compaction. input_tokens is the only supported trigger type. value must be at least 50,000 tokens.
pause_after_compactionbooleanfalseWhether to pause after generating the compaction summary
instructionsstringnullCustom summarization prompt. Completely replaces the default prompt when provided.

Trigger configuration

Configure when compaction triggers using the trigger parameter:

bash
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: compact-2026-01-12" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 4096,
      "messages": [
        {
          "role": "user",
          "content": "Hello, Haijun"
        }
      ],
      "context_management": {
        "edits": [
          {
            "type": "compact_20260112",
            "trigger": {
              "type": "input_tokens",
              "value": 150000
            }
          }
        ]
      }
    }'
bash
  ant beta:messages create --beta compact-2026-01-12 <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 4096
  messages:
    - role: user
      content: Hello, Haijun
  context_management:
    edits:
      - type: compact_20260112
        trigger:
          type: input_tokens
          value: 150000
  YAML
python
  client = juglow.Juglow()
  messages = [{"role": "user", "content": "Hello, Haijun"}]
  response = client.beta.messages.create(
      betas=["compact-2026-01-12"],
      model="haijun-opus-5-5",
      max_tokens=4096,
      messages=messages,
      context_management={
          "edits": [
              {
                  "type": "compact_20260112",
                  "trigger": {"type": "input_tokens", "value": 150000},
              }
          ]
      },
  )
typescript
  const client = new Juglow();
  const messages: Juglow.Beta.Messages.BetaMessageParam[] = [
    { role: "user", content: "Hello, Haijun" }
  ];

  const response = await client.beta.messages.create({
    betas: ["compact-2026-01-12"],
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    messages,
    context_management: {
      edits: [
        {
          type: "compact_20260112",
          trigger: {
            type: "input_tokens",
            value: 150000
          }
        }
      ]
    }
  });
csharp
  JuglowClient client = new();
  List<BetaMessageParam> messages = [new() { Role = Role.User, Content = "Hello" }];

  var parameters = new MessageCreateParams
  {
      Model = "haijun-opus-5-5",
      MaxTokens = 4096,
      Betas = ["compact-2026-01-12"],
      Messages = messages,
      ContextManagement = new BetaContextManagementConfig
      {
          Edits = [new BetaCompact20260112Edit
          {
              Trigger = new BetaInputTokensTrigger(150000)
          }]
      }
  };

  var message = await client.Beta.Messages.Create(parameters);
  Console.WriteLine(message);
go
  client := juglow.NewClient()
  messages := []juglow.BetaMessageParam{juglow.NewBetaUserMessage(juglow.NewBetaTextBlock("Hello, Haijun"))}

  response, err := client.Beta.Messages.New(context.TODO(), juglow.BetaMessageNewParams{
  	Model:     juglow.ModelHaijunOpus5_5,
  	MaxTokens: 4096,
  	Messages:  messages,
  	ContextManagement: juglow.BetaContextManagementConfigParam{
  		Edits: []juglow.BetaContextManagementConfigEditUnionParam{
  			{OfCompact20260112: &juglow.BetaCompact20260112EditParam{
  				Trigger: juglow.BetaInputTokensTriggerParam{Value: 150000},
  			}},
  		},
  	},
  	Betas: []juglow.JuglowBeta{"compact-2026-01-12"},
  })
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println(response)
java
  import com.juglow.models.beta.messages.BetaContextManagementConfig;
  import com.juglow.models.beta.messages.BetaCompact20260112Edit;
  import com.juglow.models.beta.messages.BetaInputTokensTrigger;
  // ...
          JuglowClient client = JuglowOkHttpClient.fromEnv();

          MessageCreateParams params = MessageCreateParams.builder()
              .model("haijun-opus-5-5")
              .maxTokens(4096L)
              .addBeta("compact-2026-01-12")
              .addUserMessage("Hello, Haijun")
              .contextManagement(BetaContextManagementConfig.builder()
                  .addEdit(BetaCompact20260112Edit.builder()
                      .trigger(BetaInputTokensTrigger.builder()
                          .value(150000L)
                          .build())
                      .build())
                  .build())
              .build();

          BetaMessage response = client.beta().messages().create(params);
          System.out.println(response);
php
  $client = new Client();
  $messages = [['role' => 'user', 'content' => 'Hello, Haijun']];

  $message = $client->beta->messages->create(
      maxTokens: 4096,
      messages: $messages,
      model: 'haijun-opus-5-5',
      betas: ['compact-2026-01-12'],
      contextManagement: [
          'edits' => [
              [
                  'type' => 'compact_20260112',
                  'trigger' => [
                      'type' => 'input_tokens',
                      'value' => 150000
                  ]
              ]
          ]
      ]
  );

  echo $message;
ruby
  client = Juglow::Client.new
  messages = [{ role: "user", content: "Hello, Haijun" }]

  response = client.beta.messages.create(
    betas: ["compact-2026-01-12"],
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    messages: messages,
    context_management: {
      edits: [
        {
          type: "compact_20260112",
          trigger: {
            type: "input_tokens",
            value: 150000
          }
        }
      ]
    }
  )
  puts response

Custom summarization instructions

The default summarization prompt varies by model. Each default instructs Haijun to write a summary inside

tags with the information needed to continue the task in a future context window. For example, some models use the following prompt:

text
You have written a partial transcript for the initial task above. Please write a summary of the transcript. The purpose of this summary is to provide continuity so you can continue to make progress towards solving the task in a future context, where the raw history above may not be accessible and will be replaced with this summary. Write down anything that would be helpful, including the state, next steps, learnings etc. You must wrap your summary in a <summary></summary> block.

You can provide custom instructions through the instructions parameter. Custom instructions don't supplement the default prompt. They replace it completely:

bash
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: compact-2026-01-12" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 4096,
      "messages": [
        {
          "role": "user",
          "content": "Hello, Haijun"
        }
      ],
      "context_management": {
        "edits": [
          {
            "type": "compact_20260112",
            "instructions": "Focus on preserving code snippets, variable names, and technical decisions."
          }
        ]
      }
    }'
bash
  ant beta:messages create --beta compact-2026-01-12 <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 4096
  messages:
    - role: user
      content: Hello, Haijun
  context_management:
    edits:
      - type: compact_20260112
        instructions: >-
          Focus on preserving code snippets, variable names, and
          technical decisions.
  YAML
python
  client = juglow.Juglow()
  messages = [{"role": "user", "content": "Hello, Haijun"}]
  response = client.beta.messages.create(
      betas=["compact-2026-01-12"],
      model="haijun-opus-5-5",
      max_tokens=4096,
      messages=messages,
      context_management={
          "edits": [
              {
                  "type": "compact_20260112",
                  "instructions": "Focus on preserving code snippets, variable names, and technical decisions.",
              }
          ]
      },
  )
typescript
  const client = new Juglow();
  const messages: Juglow.Beta.Messages.BetaMessageParam[] = [
    { role: "user", content: "Hello, Haijun" }
  ];

  const response = await client.beta.messages.create({
    betas: ["compact-2026-01-12"],
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    messages,
    context_management: {
      edits: [
        {
          type: "compact_20260112",
          instructions:
            "Focus on preserving code snippets, variable names, and technical decisions."
        }
      ]
    }
  });
csharp
  JuglowClient client = new();

  var parameters = new MessageCreateParams
  {
      Betas = ["compact-2026-01-12"],
      Model = "haijun-opus-5-5",
      MaxTokens = 4096,
      Messages =
      [
          new BetaMessageParam { Role = Role.User, Content = "Help me build a Python web scraper" },
          new BetaMessageParam { Role = Role.Assistant, Content = "I'll help you build a web scraper..." },
          new BetaMessageParam { Role = Role.User, Content = "Add support for JavaScript-rendered pages" }
      ],
      ContextManagement = new BetaContextManagementConfig
      {
          Edits = [new BetaCompact20260112Edit
          {
              Instructions = "Focus on preserving code snippets, variable names, and technical decisions."
          }]
      }
  };

  var message = await client.Beta.Messages.Create(parameters);
  Console.WriteLine(message);
go
  client := juglow.NewClient()

  response, err := client.Beta.Messages.New(context.TODO(), juglow.BetaMessageNewParams{
  	Model:     juglow.ModelHaijunOpus5_5,
  	MaxTokens: 4096,
  	Messages: []juglow.BetaMessageParam{
  		juglow.NewBetaUserMessage(juglow.NewBetaTextBlock("Help me build a Python web scraper")),
  		{Role: juglow.BetaMessageParamRoleAssistant, Content: []juglow.BetaContentBlockParamUnion{juglow.NewBetaTextBlock("I'll help you build a web scraper...")}},
  		juglow.NewBetaUserMessage(juglow.NewBetaTextBlock("Add support for JavaScript-rendered pages")),
  	},
  	ContextManagement: juglow.BetaContextManagementConfigParam{
  		Edits: []juglow.BetaContextManagementConfigEditUnionParam{
  			{OfCompact20260112: &juglow.BetaCompact20260112EditParam{
  				Instructions: juglow.String("Focus on preserving code snippets, variable names, and technical decisions."),
  			}},
  		},
  	},
  	Betas: []juglow.JuglowBeta{"compact-2026-01-12"},
  })
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println(response)
java
  import com.juglow.models.beta.messages.BetaContextManagementConfig;
  import com.juglow.models.beta.messages.BetaCompact20260112Edit;
  // ...
          JuglowClient client = JuglowOkHttpClient.fromEnv();

          MessageCreateParams params = MessageCreateParams.builder()
              .addBeta("compact-2026-01-12")
              .model("haijun-opus-5-5")
              .maxTokens(4096L)
              .addUserMessage("Help me build a Python web scraper")
              .addAssistantMessage("I'll help you build a web scraper...")
              .addUserMessage("Add support for JavaScript-rendered pages")
              .contextManagement(BetaContextManagementConfig.builder()
                  .addEdit(BetaCompact20260112Edit.builder()
                      .instructions("Focus on preserving code snippets, variable names, and technical decisions.")
                      .build())
                  .build())
              .build();

          BetaMessage response = client.beta().messages().create(params);
          System.out.println(response);
php
  $client = new Client();

  $response = $client->beta->messages->create(
      maxTokens: 4096,
      messages: [
          ['role' => 'user', 'content' => 'Help me build a Python web scraper'],
          ['role' => 'assistant', 'content' => "I'll help you build a web scraper..."],
          ['role' => 'user', 'content' => 'Add support for JavaScript-rendered pages']
      ],
      model: 'haijun-opus-5-5',
      betas: ['compact-2026-01-12'],
      contextManagement: [
          'edits' => [
              [
                  'type' => 'compact_20260112',
                  'instructions' => 'Focus on preserving code snippets, variable names, and technical decisions.'
              ]
          ]
      ]
  );

  echo json_encode($response, JSON_PRETTY_PRINT), PHP_EOL;
ruby
  client = Juglow::Client.new

  response = client.beta.messages.create(
    betas: ["compact-2026-01-12"],
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    messages: [
      { role: "user", content: "Help me build a Python web scraper" },
      { role: "assistant", content: "I'll help you build a web scraper..." },
      { role: "user", content: "Add support for JavaScript-rendered pages" }
    ],
    context_management: {
      edits: [
        {
          type: "compact_20260112",
          instructions:
            "Focus on preserving code snippets, variable names, and technical decisions."
        }
      ]
    }
  )

  puts response

On Haijun 5.1 and later models, a request with custom instructions summarizes from the visible conversation only: earlier thinking blocks are not part of the summarizer's input.

Pausing after compaction

Use pause_after_compaction to pause the API after generating the compaction summary. This allows you to add additional content blocks (such as preserving recent messages or specific instruction-oriented messages) before the API continues with the response.

When enabled, the API returns a message with the compaction stop reason after generating the compaction block:

bash
  # pause_after_compaction stops the response right after the compaction
  # summary so you can adjust the messages before continuing. The continue
  # step doesn't translate well to a one-off shell command; see the SDK tabs
  # for the full pause-and-continue flow. Single paused request:
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: compact-2026-01-12" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 4096,
      "messages": [
        {
          "role": "user",
          "content": "Hello, Haijun"
        }
      ],
      "context_management": {
        "edits": [
          {
            "type": "compact_20260112",
            "pause_after_compaction": true
          }
        ]
      }
    }'
bash
  # pause_after_compaction stops the response right after the compaction
  # summary so you can adjust the messages before continuing. The continue
  # step doesn't translate well to a one-off CLI command; see the SDK tabs
  # for the full pause-and-continue flow. Single paused request:
  ant beta:messages create --beta compact-2026-01-12 --format jsonl <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 4096
  messages:
    - role: user
      content: Hello, Haijun
  context_management:
    edits:
      - type: compact_20260112
        pause_after_compaction: true
  YAML
python
  client = juglow.Juglow()
  messages = [{"role": "user", "content": "Hello, Haijun"}]
  response = client.beta.messages.create(
      betas=["compact-2026-01-12"],
      model="haijun-opus-5-5",
      max_tokens=4096,
      messages=messages,
      context_management={
          "edits": [{"type": "compact_20260112", "pause_after_compaction": True}]
      },
  )

  # Check if compaction triggered a pause
  if response.stop_reason == "compaction":
      # Response contains only the compaction block
      messages.append({"role": "assistant", "content": response.content})

      # Continue the request
      response = client.beta.messages.create(
          betas=["compact-2026-01-12"],
          model="haijun-opus-5-5",
          max_tokens=4096,
          messages=messages,
          context_management={"edits": [{"type": "compact_20260112"}]},
      )
typescript
  const client = new Juglow();
  const messages: Juglow.Beta.Messages.BetaMessageParam[] = [
    { role: "user", content: "Hello, Haijun" }
  ];

  let response = await client.beta.messages.create({
    betas: ["compact-2026-01-12"],
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    messages,
    context_management: {
      edits: [
        {
          type: "compact_20260112",
          pause_after_compaction: true
        }
      ]
    }
  });

  // Check if compaction triggered a pause
  if (response.stop_reason === "compaction") {
    // Response contains only the compaction block
    messages.push({
      role: "assistant",
      content: response.content
    });

    // Continue the request
    response = await client.beta.messages.create({
      betas: ["compact-2026-01-12"],
      model: "haijun-opus-5-5",
      max_tokens: 4096,
      messages,
      context_management: {
        edits: [{ type: "compact_20260112" }]
      }
    });
  }
csharp
  var client = new JuglowClient();
  var messages = new List<BetaMessageParam>
  {
      new() { Role = Role.User, Content = "Hello, Haijun" }
  };

  var parameters = new MessageCreateParams
  {
      Model = "haijun-opus-5-5",
      MaxTokens = 4096,
      Betas = ["compact-2026-01-12"],
      Messages = messages,
      ContextManagement = new BetaContextManagementConfig
      {
          Edits = [new BetaCompact20260112Edit
          {
              PauseAfterCompaction = true
          }]
      }
  };

  var response = await client.Beta.Messages.Create(parameters);

  if (response.StopReason == BetaStopReason.Compaction)
  {
      messages.Add(new BetaMessageParam
      {
          Role = Role.Assistant,
          Content = response.Content.Select(block => new BetaContentBlockParam(block.Json)).ToList()
      });

      parameters = new()
      {
          Model = "haijun-opus-5-5",
          MaxTokens = 4096,
          Betas = ["compact-2026-01-12"],
          Messages = messages,
          ContextManagement = new BetaContextManagementConfig
          {
              Edits = [new BetaCompact20260112Edit()]
          }
      };

      response = await client.Beta.Messages.Create(parameters);
  }

  Console.WriteLine(response);
go
  client := juglow.NewClient()
  messages := []juglow.BetaMessageParam{juglow.NewBetaUserMessage(juglow.NewBetaTextBlock("Hello, Haijun"))}

  compactEdit := juglow.BetaContextManagementConfigParam{
  	Edits: []juglow.BetaContextManagementConfigEditUnionParam{
  		{OfCompact20260112: &juglow.BetaCompact20260112EditParam{
  			PauseAfterCompaction: juglow.Bool(true),
  		}},
  	},
  }

  response, err := client.Beta.Messages.New(context.TODO(), juglow.BetaMessageNewParams{
  	Model:             juglow.ModelHaijunOpus5_5,
  	MaxTokens:         4096,
  	Messages:          messages,
  	ContextManagement: compactEdit,
  	Betas:             []juglow.JuglowBeta{"compact-2026-01-12"},
  })
  if err != nil {
  	log.Fatal(err)
  }

  if response.StopReason == "compaction" {
  	messages = append(messages, response.ToParam())

  	response, err = client.Beta.Messages.New(context.TODO(), juglow.BetaMessageNewParams{
  		Model:     juglow.ModelHaijunOpus5_5,
  		MaxTokens: 4096,
  		Messages:  messages,
  		ContextManagement: juglow.BetaContextManagementConfigParam{
  			Edits: []juglow.BetaContextManagementConfigEditUnionParam{
  				{OfCompact20260112: &juglow.BetaCompact20260112EditParam{}},
  			},
  		},
  		Betas: []juglow.JuglowBeta{"compact-2026-01-12"},
  	})
  	if err != nil {
  		log.Fatal(err)
  	}
  }

  fmt.Println(response)
java
  import com.juglow.models.beta.messages.BetaContextManagementConfig;
  import com.juglow.models.beta.messages.BetaCompact20260112Edit;
  import com.juglow.models.beta.messages.BetaStopReason;
  // ...
          JuglowClient client = JuglowOkHttpClient.fromEnv();

          MessageCreateParams params = MessageCreateParams.builder()
              .model("haijun-opus-5-5")
              .maxTokens(4096L)
              .addBeta("compact-2026-01-12")
              .addUserMessage("Help me build a website")
              .contextManagement(BetaContextManagementConfig.builder()
                  .addEdit(BetaCompact20260112Edit.builder()
                      .pauseAfterCompaction(true)
                      .build())
                  .build())
              .build();

          BetaMessage response = client.beta().messages().create(params);

          // Check if compaction triggered a pause
          if (response.stopReason().isPresent()
                  && response.stopReason().get().equals(BetaStopReason.COMPACTION)) {
              // Append the compaction block and continue the request
              // by building a new request with the compacted context
              MessageCreateParams continueParams = MessageCreateParams.builder()
                  .model("haijun-opus-5-5")
                  .maxTokens(4096L)
                  .addBeta("compact-2026-01-12")
                  .addUserMessage("Help me build a website")
                  .addMessage(response)
                  .contextManagement(BetaContextManagementConfig.builder()
                      .addEdit(BetaCompact20260112Edit.builder().build())
                      .build())
                  .build();

              response = client.beta().messages().create(continueParams);
          }

          System.out.println(response);
php
  $client = new Client();
  $messages = [['role' => 'user', 'content' => 'Hello, Haijun']];

  $response = $client->beta->messages->create(
      maxTokens: 4096,
      messages: $messages,
      model: 'haijun-opus-5-5',
      betas: ['compact-2026-01-12'],
      contextManagement: [
          'edits' => [
              [
                  'type' => 'compact_20260112',
                  'pause_after_compaction' => true
              ]
          ]
      ]
  );

  if ($response->stopReason === 'compaction') {
      $messages[] = [
          'role' => 'assistant',
          'content' => $response->content
      ];

      $response = $client->beta->messages->create(
          maxTokens: 4096,
          messages: $messages,
          model: 'haijun-opus-5-5',
          betas: ['compact-2026-01-12'],
          contextManagement: [
              'edits' => [
                  ['type' => 'compact_20260112']
              ]
          ]
      );
  }

  echo $response;
ruby
  client = Juglow::Client.new
  messages = [{ role: "user", content: "Hello, Haijun" }]

  response = client.beta.messages.create(
    betas: ["compact-2026-01-12"],
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    messages: messages,
    context_management: {
      edits: [
        {
          type: "compact_20260112",
          pause_after_compaction: true
        }
      ]
    }
  )

  if response.stop_reason == :compaction
    messages << { role: "assistant", content: response.content }

    response = client.beta.messages.create(
      betas: ["compact-2026-01-12"],
      model: "haijun-opus-5-5",
      max_tokens: 4096,
      messages: messages,
      context_management: {
        edits: [{ type: "compact_20260112" }]
      }
    )
  end

  puts response

Enforcing a total token budget

When a model works on long tasks with many tool-use iterations, total token consumption can grow significantly. You can combine pause_after_compaction with a compaction counter to estimate cumulative usage and gracefully wrap up the task once a budget is reached.

This example appears in the SDK languages only: its value is the budget-tracking logic around the request. The raw request combines the trigger from Trigger configuration with pause_after_compaction from Pausing after compaction.

python
  client = juglow.Juglow()
  messages = [{"role": "user", "content": "Hello, Haijun"}]
  TRIGGER_THRESHOLD = 100_000
  TOTAL_TOKEN_BUDGET = 3_000_000
  n_compactions = 0

  response = client.beta.messages.create(
      betas=["compact-2026-01-12"],
      model="haijun-opus-5-5",
      max_tokens=4096,
      messages=messages,
      context_management={
          "edits": [
              {
                  "type": "compact_20260112",
                  "trigger": {"type": "input_tokens", "value": TRIGGER_THRESHOLD},
                  "pause_after_compaction": True,
              }
          ]
      },
  )

  if response.stop_reason == "compaction":
      n_compactions += 1
      messages.append({"role": "assistant", "content": response.content})

      # Estimate total tokens consumed; prompt wrap-up if over budget
      if n_compactions * TRIGGER_THRESHOLD >= TOTAL_TOKEN_BUDGET:
          messages.append(
              {
                  "role": "user",
                  "content": "Please wrap up your current work and summarize the final state.",
              }
          )
typescript
  const client = new Juglow();
  const messages: Juglow.Beta.Messages.BetaMessageParam[] = [
    { role: "user", content: "Hello, Haijun" }
  ];
  const TRIGGER_THRESHOLD = 100_000;
  const TOTAL_TOKEN_BUDGET = 3_000_000;
  let compactionCount = 0;

  const response = await client.beta.messages.create({
    betas: ["compact-2026-01-12"],
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    messages,
    context_management: {
      edits: [
        {
          type: "compact_20260112",
          trigger: { type: "input_tokens", value: TRIGGER_THRESHOLD },
          pause_after_compaction: true
        }
      ]
    }
  });

  if (response.stop_reason === "compaction") {
    compactionCount += 1;
    messages.push({ role: "assistant", content: response.content });

    // Estimate total tokens consumed; prompt wrap-up if over budget
    if (compactionCount * TRIGGER_THRESHOLD >= TOTAL_TOKEN_BUDGET) {
      messages.push({
        role: "user",
        content: "Please wrap up your current work and summarize the final state."
      });
    }
  }
csharp
  JuglowClient client = new();
  List<BetaMessageParam> messages = [new() { Role = Role.User, Content = "Hello, Haijun" }];

  const int TriggerThreshold = 100_000;
  const int TotalTokenBudget = 3_000_000;
  int compactionCount = 0;

  var response = await client.Beta.Messages.Create(new()
  {
      Betas = ["compact-2026-01-12"],
      Model = "haijun-opus-5-5",
      MaxTokens = 4096,
      Messages = messages,
      ContextManagement = new BetaContextManagementConfig
      {
          Edits = [new BetaCompact20260112Edit
          {
              Trigger = new BetaInputTokensTrigger(TriggerThreshold),
              PauseAfterCompaction = true
          }]
      }
  });

  if (response.StopReason == BetaStopReason.Compaction)
  {
      compactionCount += 1;
      messages.Add(new()
      {
          Role = Role.Assistant,
          Content = response.Content.Select(b => new BetaContentBlockParam(b.Json)).ToList()
      });

      // Estimate total tokens consumed; prompt wrap-up if over budget
      if (compactionCount * TriggerThreshold >= TotalTokenBudget)
      {
          messages.Add(new()
          {
              Role = Role.User,
              Content = "Please wrap up your current work and summarize the final state."
          });
      }
  }

  Console.WriteLine(response);
go
  client := juglow.NewClient()
  messages := []juglow.BetaMessageParam{juglow.NewBetaUserMessage(juglow.NewBetaTextBlock("Hello, Haijun"))}

  const triggerThreshold = 100_000
  const totalTokenBudget = 3_000_000
  compactionCount := 0

  response, err := client.Beta.Messages.New(context.TODO(), juglow.BetaMessageNewParams{
  	Model:     juglow.ModelHaijunOpus5_5,
  	MaxTokens: 4096,
  	Messages:  messages,
  	ContextManagement: juglow.BetaContextManagementConfigParam{
  		Edits: []juglow.BetaContextManagementConfigEditUnionParam{
  			{OfCompact20260112: &juglow.BetaCompact20260112EditParam{
  				Trigger:              juglow.BetaInputTokensTriggerParam{Value: triggerThreshold},
  				PauseAfterCompaction: juglow.Bool(true),
  			}},
  		},
  	},
  	Betas: []juglow.JuglowBeta{"compact-2026-01-12"},
  })
  if err != nil {
  	log.Fatal(err)
  }

  if response.StopReason == "compaction" {
  	compactionCount++
  	messages = append(messages, response.ToParam())

  	// Estimate total tokens consumed; prompt wrap-up if over budget
  	if compactionCount*triggerThreshold >= totalTokenBudget {
  		messages = append(messages, juglow.NewBetaUserMessage(juglow.NewBetaTextBlock("Please wrap up your current work and summarize the final state.")))
  	}
  }

  fmt.Println(response)
java
  import com.juglow.models.beta.messages.BetaContextManagementConfig;
  import com.juglow.models.beta.messages.BetaCompact20260112Edit;
  import com.juglow.models.beta.messages.BetaInputTokensTrigger;
  import com.juglow.models.beta.messages.BetaStopReason;
  // ...
          JuglowClient client = JuglowOkHttpClient.fromEnv();

          long triggerThreshold = 100_000;
          long totalTokenBudget = 3_000_000;
          int compactionCount = 0;

          List<BetaMessageParam> messages = new ArrayList<>();
          messages.add(BetaMessageParam.builder()
              .role(BetaMessageParam.Role.USER)
              .content("Hello, Haijun")
              .build());

          MessageCreateParams params = MessageCreateParams.builder()
              .addBeta("compact-2026-01-12")
              .model("haijun-opus-5-5")
              .maxTokens(4096L)
              .messages(messages)
              .contextManagement(BetaContextManagementConfig.builder()
                  .addEdit(BetaCompact20260112Edit.builder()
                      .trigger(BetaInputTokensTrigger.builder()
                          .value(triggerThreshold)
                          .build())
                      .pauseAfterCompaction(true)
                      .build())
                  .build())
              .build();

          BetaMessage response = client.beta().messages().create(params);

          if (response.stopReason().isPresent()
                  && response.stopReason().get().equals(BetaStopReason.COMPACTION)) {
              compactionCount += 1;
              messages.add(response.toParam());

              // Estimate total tokens consumed; prompt wrap-up if over budget
              if (compactionCount * triggerThreshold >= totalTokenBudget) {
                  messages.add(BetaMessageParam.builder()
                      .role(BetaMessageParam.Role.USER)
                      .content("Please wrap up your current work and summarize the final state.")
                      .build());
              }
          }

          System.out.println(response);
php
  $client = new Client();

  $triggerThreshold = 100_000;
  $totalTokenBudget = 3_000_000;
  $compactionCount = 0;

  $messages = [['role' => 'user', 'content' => 'Hello, Haijun']];

  $response = $client->beta->messages->create(
      maxTokens: 4096,
      messages: $messages,
      model: 'haijun-opus-5-5',
      betas: ['compact-2026-01-12'],
      contextManagement: [
          'edits' => [
              [
                  'type' => 'compact_20260112',
                  'trigger' => ['type' => 'input_tokens', 'value' => $triggerThreshold],
                  'pause_after_compaction' => true
              ]
          ]
      ]
  );

  if ($response->stopReason === 'compaction') {
      $compactionCount += 1;
      $messages[] = ['role' => 'assistant', 'content' => $response->content];

      // Estimate total tokens consumed; prompt wrap-up if over budget
      if ($compactionCount * $triggerThreshold >= $totalTokenBudget) {
          $messages[] = [
              'role' => 'user',
              'content' => 'Please wrap up your current work and summarize the final state.'
          ];
      }
  }
ruby
  client = Juglow::Client.new
  messages = [{ role: "user", content: "Hello, Haijun" }]
  TRIGGER_THRESHOLD = 100_000
  TOTAL_TOKEN_BUDGET = 3_000_000
  compaction_count = 0

  response = client.beta.messages.create(
    betas: ["compact-2026-01-12"],
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    messages: messages,
    context_management: {
      edits: [
        {
          type: "compact_20260112",
          trigger: { type: "input_tokens", value: TRIGGER_THRESHOLD },
          pause_after_compaction: true
        }
      ]
    }
  )

  if response.stop_reason == :compaction
    compaction_count += 1
    messages << { role: "assistant", content: response.content }

    # Estimate total tokens consumed; prompt wrap-up if over budget
    if compaction_count * TRIGGER_THRESHOLD >= TOTAL_TOKEN_BUDGET
      messages << {
        role: "user",
        content: "Please wrap up your current work and summarize the final state."
      }
    end
  end

Working with compaction blocks

When compaction is triggered, the API returns a compaction block at the start of the assistant response.

A long-running conversation might result in multiple compactions. The last compaction block reflects the final state of the prompt, replacing content prior to it with the generated summary.

json
{
  "content": [
    {
      "type": "compaction",
      "content": "Summary of the conversation: The user requested help building a web scraper..."
    },
    {
      "type": "text",
      "text": "Based on our conversation so far..."
    }
  ]
}

Passing compaction blocks back

You must pass the compaction block back to the API on subsequent requests to continue the conversation with the shortened prompt. The simplest approach is to append the entire response content to your messages:

bash
  # The response content, including the compaction block, must go back to the
  # API as the assistant turn of the next request. Managing that message list
  # doesn't translate well to a one-off shell command; see the CLI and SDK
  # tabs for the full flow. First request:
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: compact-2026-01-12" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 4096,
      "messages": [
        {
          "role": "user",
          "content": "Hello, Haijun"
        }
      ],
      "context_management": {
        "edits": [
          {
            "type": "compact_20260112"
          }
        ]
      }
    }'
bash
  ant beta:messages create \
    --beta compact-2026-01-12 \
    --transform content \
    --format jsonl <<'YAML' > content.json
  model: haijun-opus-5-5
  max_tokens: 4096
  messages:
    - role: user
      content: Hello, Haijun
  context_management:
    edits:
      - type: compact_20260112
  YAML

  # After receiving a response with a compaction block, append it as the
  # assistant turn and continue the conversation
  ant beta:messages create --beta compact-2026-01-12 <<YAML
  model: haijun-opus-5-5
  max_tokens: 4096
  messages:
    - role: user
      content: Hello, Haijun
    - role: assistant
      content: $(cat content.json)
    - role: user
      content: Now add error handling
  context_management:
    edits:
      - type: compact_20260112
  YAML
python
  client = juglow.Juglow()
  messages = [{"role": "user", "content": "Hello, Haijun"}]
  response = client.beta.messages.create(
      betas=["compact-2026-01-12"],
      model="haijun-opus-5-5",
      max_tokens=4096,
      messages=messages,
      context_management={"edits": [{"type": "compact_20260112"}]},
  )
  # After receiving a response with a compaction block
  messages.append({"role": "assistant", "content": response.content})

  # Continue the conversation
  messages.append({"role": "user", "content": "Now add error handling"})

  response = client.beta.messages.create(
      betas=["compact-2026-01-12"],
      model="haijun-opus-5-5",
      max_tokens=4096,
      messages=messages,
      context_management={"edits": [{"type": "compact_20260112"}]},
  )
typescript
  const client = new Juglow();
  const messages: Juglow.Beta.Messages.BetaMessageParam[] = [
    { role: "user", content: "Hello, Haijun" }
  ];

  const response = await client.beta.messages.create({
    betas: ["compact-2026-01-12"],
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    messages,
    context_management: {
      edits: [{ type: "compact_20260112" }]
    }
  });

  // After receiving a response with a compaction block
  messages.push({
    role: "assistant",
    content: response.content
  });

  // Continue the conversation
  messages.push({ role: "user", content: "Now add error handling" });

  const nextResponse = await client.beta.messages.create({
    betas: ["compact-2026-01-12"],
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    messages,
    context_management: {
      edits: [{ type: "compact_20260112" }]
    }
  });
csharp
  JuglowClient client = new();

  var messages = new List<BetaMessageParam>
  {
      new() { Role = Role.User, Content = "Help me build a web scraper" }
  };

  var response = await client.Beta.Messages.Create(new()
  {
      Betas = ["compact-2026-01-12"],
      Model = "haijun-opus-5-5",
      MaxTokens = 4096,
      Messages = messages,
      ContextManagement = new BetaContextManagementConfig
      {
          Edits = [new BetaCompact20260112Edit()]
      }
  });

  messages.Add(new BetaMessageParam
  {
      Role = Role.Assistant,
      Content = response.Content.Select(block => new BetaContentBlockParam(block.Json)).ToList()
  });

  messages.Add(new BetaMessageParam { Role = Role.User, Content = "Now add error handling" });

  var nextResponse = await client.Beta.Messages.Create(new()
  {
      Betas = ["compact-2026-01-12"],
      Model = "haijun-opus-5-5",
      MaxTokens = 4096,
      Messages = messages,
      ContextManagement = new BetaContextManagementConfig
      {
          Edits = [new BetaCompact20260112Edit()]
      }
  });

  Console.WriteLine(nextResponse);
go
  client := juglow.NewClient()

  messages := []juglow.BetaMessageParam{
  	juglow.NewBetaUserMessage(juglow.NewBetaTextBlock("Help me build a web scraper")),
  }

  compactEdit := juglow.BetaContextManagementConfigParam{
  	Edits: []juglow.BetaContextManagementConfigEditUnionParam{
  		{OfCompact20260112: &juglow.BetaCompact20260112EditParam{}},
  	},
  }

  response, err := client.Beta.Messages.New(context.TODO(), juglow.BetaMessageNewParams{
  	Model:             juglow.ModelHaijunOpus5_5,
  	MaxTokens:         4096,
  	Messages:          messages,
  	ContextManagement: compactEdit,
  	Betas:             []juglow.JuglowBeta{"compact-2026-01-12"},
  })
  if err != nil {
  	log.Fatal(err)
  }

  messages = append(messages, response.ToParam())

  messages = append(messages, juglow.NewBetaUserMessage(juglow.NewBetaTextBlock("Now add error handling")))

  nextResponse, err := client.Beta.Messages.New(context.TODO(), juglow.BetaMessageNewParams{
  	Model:             juglow.ModelHaijunOpus5_5,
  	MaxTokens:         4096,
  	Messages:          messages,
  	ContextManagement: compactEdit,
  	Betas:             []juglow.JuglowBeta{"compact-2026-01-12"},
  })
  if err != nil {
  	log.Fatal(err)
  }

  fmt.Println(nextResponse)
java
  import com.juglow.models.beta.messages.BetaContextManagementConfig;
  import com.juglow.models.beta.messages.BetaCompact20260112Edit;
  // ...
          JuglowClient client = JuglowOkHttpClient.fromEnv();

          // First request
          BetaMessage response = client.beta().messages().create(
              MessageCreateParams.builder()
                  .addBeta("compact-2026-01-12")
                  .model("haijun-opus-5-5")
                  .maxTokens(4096L)
                  .addUserMessage("Help me build a web scraper")
                  .contextManagement(BetaContextManagementConfig.builder()
                      .addEdit(BetaCompact20260112Edit.builder().build())
                      .build())
                  .build());

          // After receiving a response with a compaction block, append the full
          // content (including compaction blocks) and continue the conversation
          BetaMessage nextResponse = client.beta().messages().create(
              MessageCreateParams.builder()
                  .addBeta("compact-2026-01-12")
                  .model("haijun-opus-5-5")
                  .maxTokens(4096L)
                  .addUserMessage("Help me build a web scraper")
                  .addMessage(response)
                  .addUserMessage("Now add error handling")
                  .contextManagement(BetaContextManagementConfig.builder()
                      .addEdit(BetaCompact20260112Edit.builder().build())
                      .build())
                  .build());

          System.out.println(nextResponse);
php
  $client = new Client();

  $messages = [
      ['role' => 'user', 'content' => 'Help me build a web scraper']
  ];

  $response = $client->beta->messages->create(
      maxTokens: 4096,
      messages: $messages,
      model: 'haijun-opus-5-5',
      betas: ['compact-2026-01-12'],
      contextManagement: [
          'edits' => [['type' => 'compact_20260112']]
      ]
  );

  $messages[] = ['role' => 'assistant', 'content' => $response->content];

  $messages[] = ['role' => 'user', 'content' => 'Now add error handling'];

  $nextResponse = $client->beta->messages->create(
      maxTokens: 4096,
      messages: $messages,
      model: 'haijun-opus-5-5',
      betas: ['compact-2026-01-12'],
      contextManagement: [
          'edits' => [['type' => 'compact_20260112']]
      ]
  );

  echo json_encode($nextResponse, JSON_PRETTY_PRINT), PHP_EOL;
ruby
  client = Juglow::Client.new

  messages = [
    { role: "user", content: "Help me build a web scraper" }
  ]

  response = client.beta.messages.create(
    betas: ["compact-2026-01-12"],
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    messages: messages,
    context_management: {
      edits: [{ type: "compact_20260112" }]
    }
  )

  messages << { role: "assistant", content: response.content }

  messages << { role: "user", content: "Now add error handling" }

  next_response = client.beta.messages.create(
    betas: ["compact-2026-01-12"],
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    messages: messages,
    context_management: {
      edits: [{ type: "compact_20260112" }]
    }
  )

  puts next_response.content

In Python, use client.beta.messages, as the samples on this page do. If you call client.messages and serialize blocks yourself, a plain model_dump() adds text: null and citations: null to the compaction block. The API then rejects the request with a 400 error (Extra inputs are not permitted). Use to_dict() or model_dump(exclude_none=True) instead. Continue from the summary gives the same advice for on-demand compaction.

When the API receives a compaction block, all content blocks before it are ignored. You can either:

  • Keep the original messages in your list and let the API handle removing the compacted content
  • Manually drop the compacted messages and only include the compaction block onwards

On Haijun Fable 5.1, Haijun Mythos 5.1, and Haijun Opus 5.5, thinking blocks from before a compaction block aren't carried forward, so the summary is all the model has of that earlier work. If you write your own instructions, tell the model what the summary must retain; see Tell the model what to preserve in compaction summaries.

Streaming

The compaction block streams differently from text blocks. You receive a content_block_start event, followed by a single content_block_delta with the complete summary content (no intermediate streaming), and then a content_block_stop event.

bash
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: compact-2026-01-12" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 4096,
      "stream": true,
      "messages": [
        {
          "role": "user",
          "content": "Hello, Haijun"
        }
      ],
      "context_management": {
        "edits": [
          {
            "type": "compact_20260112"
          }
        ]
      }
    }'
bash
  ant beta:messages create \
    --stream \
    --beta compact-2026-01-12 \
    --format jsonl <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 4096
  messages:
    - role: user
      content: Hello, Haijun
  context_management:
    edits:
      - type: compact_20260112
  YAML
python
  client = juglow.Juglow()
  messages = [{"role": "user", "content": "Hello, Haijun"}]

  with client.beta.messages.stream(
      betas=["compact-2026-01-12"],
      model="haijun-opus-5-5",
      max_tokens=4096,
      messages=messages,
      context_management={"edits": [{"type": "compact_20260112"}]},
  ) as stream:
      for event in stream:
          match event.type:
              case "content_block_start":
                  block = event.content_block
                  match block.type:
                      case "compaction":
                          print("Compaction started...")
                      case "text":
                          print("Text response started...")

              case "content_block_delta":
                  delta = event.delta
                  match delta.type:
                      case "compaction_delta":
                          print(f"Compaction complete: {len(delta.content or '')} chars")
                      case "text_delta":
                          print(delta.text, end="", flush=True)

      # Get the final accumulated message
      message = stream.get_final_message()
      messages.append({"role": "assistant", "content": message.content})
typescript
  const client = new Juglow();
  const messages: Juglow.Beta.Messages.BetaMessageParam[] = [
    { role: "user", content: "Hello, Haijun" }
  ];

  const stream = await client.beta.messages.stream({
    betas: ["compact-2026-01-12"],
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    messages,
    context_management: {
      edits: [{ type: "compact_20260112" }]
    }
  });

  for await (const event of stream) {
    switch (event.type) {
      case "content_block_start":
        switch (event.content_block.type) {
          case "compaction":
            console.log("Compaction started...");
            break;
          case "text":
            console.log("Text response started...");
            break;
        }
        break;
      case "content_block_delta":
        switch (event.delta.type) {
          case "compaction_delta":
            console.log(`Compaction complete: ${event.delta.content?.length ?? 0} chars`);
            break;
          case "text_delta":
            process.stdout.write(event.delta.text);
            break;
        }
        break;
    }
  }

  // Get the final accumulated message
  const message = await stream.finalMessage();
  messages.push({
    role: "assistant",
    content: message.content
  });
csharp
  var client = new JuglowClient();
  List<BetaMessageParam> messages = [new() { Role = Role.User, Content = "Hello" }];

  var parameters = new MessageCreateParams
  {
      Betas = ["compact-2026-01-12"],
      Model = "haijun-opus-5-5",
      MaxTokens = 4096,
      Messages = messages,
      ContextManagement = new BetaContextManagementConfig
      {
          Edits = [new BetaCompact20260112Edit()]
      }
  };

  await foreach (var streamEvent in client.Beta.Messages.CreateStreaming(parameters))
  {
      if (streamEvent.TryPickContentBlockStart(out var startEvent))
      {
          if (startEvent.ContentBlock.TryPickBetaCompaction(out _))
          {
              Console.WriteLine("Compaction started...");
          }
          else if (startEvent.ContentBlock.TryPickBetaText(out _))
          {
              Console.WriteLine("Text response started...");
          }
      }
      else if (streamEvent.TryPickContentBlockDelta(out var deltaEvent))
      {
          if (deltaEvent.Delta.TryPickCompaction(out var compactionDelta))
          {
              Console.WriteLine($"Compaction complete: {compactionDelta.Content?.Length ?? 0} chars");
          }
          else if (deltaEvent.Delta.TryPickText(out var textDelta))
          {
              Console.Write(textDelta.Text);
          }
      }
  }
go
  client := juglow.NewClient()
  messages := []juglow.BetaMessageParam{juglow.NewBetaUserMessage(juglow.NewBetaTextBlock("Hello, Haijun"))}

  stream := client.Beta.Messages.NewStreaming(context.TODO(), juglow.BetaMessageNewParams{
  	Model:     juglow.ModelHaijunOpus5_5,
  	MaxTokens: 4096,
  	Messages:  messages,
  	ContextManagement: juglow.BetaContextManagementConfigParam{
  		Edits: []juglow.BetaContextManagementConfigEditUnionParam{
  			{OfCompact20260112: &juglow.BetaCompact20260112EditParam{}},
  		},
  	},
  	Betas: []juglow.JuglowBeta{"compact-2026-01-12"},
  })

  for stream.Next() {
  	event := stream.Current()
  	switch eventVariant := event.AsAny().(type) {
  	case juglow.BetaRawContentBlockStartEvent:
  		switch eventVariant.ContentBlock.AsAny().(type) {
  		case juglow.BetaCompactionBlock:
  			fmt.Println("Compaction started...")
  		case juglow.BetaTextBlock:
  			fmt.Println("Text response started...")
  		}
  	case juglow.BetaRawContentBlockDeltaEvent:
  		switch deltaVariant := eventVariant.Delta.AsAny().(type) {
  		case juglow.BetaCompactionContentBlockDelta:
  			fmt.Printf("Compaction complete: %d chars\n", len(deltaVariant.Content))
  		case juglow.BetaTextDelta:
  			fmt.Print(deltaVariant.Text)
  		}
  	}
  }
  if err := stream.Err(); err != nil {
  	log.Fatal(err)
  }
java
  import com.juglow.models.beta.messages.BetaContextManagementConfig;
  import com.juglow.models.beta.messages.BetaCompact20260112Edit;
  // ...
          JuglowClient client = JuglowOkHttpClient.fromEnv();

          MessageCreateParams params = MessageCreateParams.builder()
              .model("haijun-opus-5-5")
              .maxTokens(4096L)
              .addBeta("compact-2026-01-12")
              .addUserMessage("Hello, Haijun")
              .contextManagement(BetaContextManagementConfig.builder()
                  .addEdit(BetaCompact20260112Edit.builder().build())
                  .build())
              .build();

          try (var streamResponse = client.beta().messages().createStreaming(params)) {
              streamResponse.stream().forEach(event -> {
                  event.contentBlockStart().ifPresent(startEvent -> {
                      startEvent.contentBlock().compaction().ifPresent(c ->
                          System.out.println("Compaction started...")
                      );
                      startEvent.contentBlock().text().ifPresent(t ->
                          System.out.println("Text response started...")
                      );
                  });

                  event.contentBlockDelta().ifPresent(deltaEvent -> {
                      deltaEvent.delta().compaction().ifPresent(cd ->
                          System.out.println("Compaction complete: " + cd.content().map(String::length).orElse(0) + " chars")
                      );
                      deltaEvent.delta().text().ifPresent(td ->
                          System.out.print(td.text())
                      );
                  });
              });
          }
php
  use Juglow\Beta\Messages\BetaCompactionBlock;
  use Juglow\Beta\Messages\BetaCompactionContentBlockDelta;
  use Juglow\Beta\Messages\BetaRawContentBlockDeltaEvent;
  use Juglow\Beta\Messages\BetaRawContentBlockStartEvent;
  use Juglow\Beta\Messages\BetaTextBlock;
  use Juglow\Beta\Messages\BetaTextDelta;

  $client = new Client();
  $messages = [['role' => 'user', 'content' => 'Hello, Haijun']];

  $stream = $client->beta->messages->createStream(
      maxTokens: 4096,
      messages: $messages,
      model: 'haijun-opus-5-5',
      betas: ['compact-2026-01-12'],
      contextManagement: [
          'edits' => [
              ['type' => 'compact_20260112']
          ]
      ]
  );

  foreach ($stream as $event) {
      switch (true) {
          case $event instanceof BetaRawContentBlockStartEvent:
              switch (true) {
                  case $event->contentBlock instanceof BetaCompactionBlock:
                      echo "Compaction started...\n";
                      break;
                  case $event->contentBlock instanceof BetaTextBlock:
                      echo "Text response started...\n";
                      break;
              }
              break;
          case $event instanceof BetaRawContentBlockDeltaEvent:
              switch (true) {
                  case $event->delta instanceof BetaCompactionContentBlockDelta:
                      echo "Compaction complete: " . strlen($event->delta->content ?? '') . " chars\n";
                      break;
                  case $event->delta instanceof BetaTextDelta:
                      echo $event->delta->text;
                      break;
              }
              break;
      }
  }
ruby
  client = Juglow::Client.new
  messages = [{ role: "user", content: "Hello, Haijun" }]

  stream = client.beta.messages.stream(
    betas: ["compact-2026-01-12"],
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    messages: messages,
    context_management: {
      edits: [{ type: "compact_20260112" }]
    }
  )

  stream.each do |event|
    case event
    when Juglow::Models::BetaRawContentBlockStartEvent
      case event.content_block
      when Juglow::Models::BetaCompactionBlock
        puts "Compaction started..."
      when Juglow::Models::BetaTextBlock
        puts "Text response started..."
      end
    when Juglow::Models::BetaRawContentBlockDeltaEvent
      delta = event.delta
      case delta
      when Juglow::Models::BetaCompactionContentBlockDelta
        puts "Compaction complete: #{(delta.content || "").length} chars"
      when Juglow::Models::BetaTextDelta
        print delta.text
      end
    end
  end

Prompt caching

Compaction works well with prompt caching. You can add a cache_control breakpoint on compaction blocks to cache the summarized content.

json
{
  "role": "assistant",
  "content": [
    {
      "type": "compaction",
      "content": "[summary text]",
      "cache_control": { "type": "ephemeral" }
    },
    {
      "type": "text",
      "text": "Based on our conversation..."
    }
  ]
}

Maximizing cache hits with system prompts

When compaction occurs, the summary becomes new content that needs to be written to the cache. Without additional cache breakpoints, this would also invalidate any cached system prompt, requiring it to be re-cached along with the compaction summary.

To maximize cache hit rates, add a cache_control breakpoint at the end of your system prompt. This keeps the system prompt cached separately from the conversation, so when compaction occurs:

  • The system prompt cache remains valid and is read from cache
  • Only the compaction summary needs to be written as a new cache entry
bash
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: compact-2026-01-12" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 4096,
      "system": [
        {
          "type": "text",
          "text": "You are a helpful coding assistant...",
          "cache_control": {
            "type": "ephemeral"
          }
        }
      ],
      "messages": [
        {
          "role": "user",
          "content": "Hello, Haijun"
        }
      ],
      "context_management": {
        "edits": [
          {
            "type": "compact_20260112"
          }
        ]
      }
    }'
bash
  ant beta:messages create --beta compact-2026-01-12 <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 4096
  system:
    - type: text
      text: You are a helpful coding assistant...
      cache_control:
        type: ephemeral
  messages:
    - role: user
      content: Hello, Haijun
  context_management:
    edits:
      - type: compact_20260112
  YAML
python
  client = juglow.Juglow()
  messages = [{"role": "user", "content": "Hello, Haijun"}]
  response = client.beta.messages.create(
      betas=["compact-2026-01-12"],
      model="haijun-opus-5-5",
      max_tokens=4096,
      system=[
          {
              "type": "text",
              "text": "You are a helpful coding assistant...",
              "cache_control": {
                  "type": "ephemeral"
              },  # Cache the system prompt separately
          }
      ],
      messages=messages,
      context_management={"edits": [{"type": "compact_20260112"}]},
  )
typescript
  const client = new Juglow();
  const messages: Juglow.Beta.Messages.BetaMessageParam[] = [
    { role: "user", content: "Hello, Haijun" }
  ];

  const response = await client.beta.messages.create({
    betas: ["compact-2026-01-12"],
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    system: [
      {
        type: "text",
        text: "You are a helpful coding assistant...",
        cache_control: { type: "ephemeral" } // Cache the system prompt separately
      }
    ],
    messages,
    context_management: {
      edits: [{ type: "compact_20260112" }]
    }
  });
csharp
  var client = new JuglowClient();

  var parameters = new MessageCreateParams
  {
      Betas = ["compact-2026-01-12"],
      Model = "haijun-opus-5-5",
      MaxTokens = 4096,
      System = new List<BetaTextBlockParam>
      {
          new()
          {
              Text = "You are a helpful coding assistant...",
              CacheControl = new BetaCacheControlEphemeral()
          }
      },
      Messages = [new() { Role = Role.User, Content = "Hello, Haijun" }],
      ContextManagement = new BetaContextManagementConfig
      {
          Edits = [new BetaCompact20260112Edit()]
      }
  };

  var response = await client.Beta.Messages.Create(parameters);
  Console.WriteLine(response);
go
  client := juglow.NewClient()

  response, err := client.Beta.Messages.New(context.TODO(), juglow.BetaMessageNewParams{
  	Model:     juglow.ModelHaijunOpus5_5,
  	MaxTokens: 4096,
  	System: []juglow.BetaTextBlockParam{
  		{
  			Text:         "You are a helpful coding assistant...",
  			CacheControl: juglow.NewBetaCacheControlEphemeralParam(),
  		},
  	},
  	Messages: []juglow.BetaMessageParam{juglow.NewBetaUserMessage(juglow.NewBetaTextBlock("Hello, Haijun"))},
  	ContextManagement: juglow.BetaContextManagementConfigParam{
  		Edits: []juglow.BetaContextManagementConfigEditUnionParam{
  			{OfCompact20260112: &juglow.BetaCompact20260112EditParam{}},
  		},
  	},
  	Betas: []juglow.JuglowBeta{"compact-2026-01-12"},
  })
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println(response)
java
  import com.juglow.models.beta.messages.BetaContextManagementConfig;
  import com.juglow.models.beta.messages.BetaCompact20260112Edit;
  import com.juglow.models.beta.messages.BetaCacheControlEphemeral;
  // ...
          JuglowClient client = JuglowOkHttpClient.fromEnv();

          MessageCreateParams params = MessageCreateParams.builder()
              .model("haijun-opus-5-5")
              .maxTokens(4096L)
              .addBeta("compact-2026-01-12")
              .systemOfBetaTextBlockParams(List.of(
                  BetaTextBlockParam.builder()
                      .text("You are a helpful coding assistant...")
                      .cacheControl(BetaCacheControlEphemeral.builder().build())
                      .build()
              ))
              .addUserMessage("Hello, Haijun")
              .contextManagement(BetaContextManagementConfig.builder()
                  .addEdit(BetaCompact20260112Edit.builder().build())
                  .build())
              .build();

          BetaMessage response = client.beta().messages().create(params);
          System.out.println(response);
php
  $client = new Client();

  $response = $client->beta->messages->create(
      maxTokens: 4096,
      messages: [['role' => 'user', 'content' => 'Hello, Haijun']],
      model: 'haijun-opus-5-5',
      betas: ['compact-2026-01-12'],
      system: [
          [
              'type' => 'text',
              'text' => 'You are a helpful coding assistant...',
              'cache_control' => [
                  'type' => 'ephemeral'
              ]
          ]
      ],
      contextManagement: [
          'edits' => [
              ['type' => 'compact_20260112']
          ]
      ]
  );

  echo json_encode($response, JSON_PRETTY_PRINT), PHP_EOL;
ruby
  client = Juglow::Client.new

  response = client.beta.messages.create(
    betas: ["compact-2026-01-12"],
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    system: [
      {
        type: "text",
        text: "You are a helpful coding assistant...",
        cache_control: {
          type: "ephemeral"
        }
      }
    ],
    messages: [{ role: "user", content: "Hello, Haijun" }],
    context_management: {
      edits: [{ type: "compact_20260112" }]
    }
  )
  puts response

This keeps long system prompts cached across multiple compaction events throughout a conversation.

Understanding usage

Compaction requires an additional sampling step, which contributes to rate limits and billing. The API returns detailed usage information in the response:

json
{
  "usage": {
    "input_tokens": 23000,
    "output_tokens": 1000,
    "iterations": [
      {
        "type": "compaction",
        "input_tokens": 180000,
        "output_tokens": 3500
      },
      {
        "type": "message",
        "input_tokens": 23000,
        "output_tokens": 1000
      }
    ]
  }
}

The iterations array shows usage for each sampling iteration. When compaction occurs, you'll see a compaction iteration followed by the main message iteration. The top-level input_tokens and output_tokens match the message iteration exactly in this example because there is only one non-compaction iteration. The final iteration's token counts reflect the effective context size after compaction.

Note: The top-level input_tokens and output_tokens do not include compaction iteration usage. They reflect the sum of all non-compaction iterations. To calculate total tokens consumed and billed for a request, sum across all entries in the usage.iterations array. If you previously relied on usage.input_tokens and usage.output_tokens for cost tracking or auditing, you'll need to update your tracking logic to aggregate across usage.iterations when compaction is enabled. With the compaction beta enabled, every response includes usage.iterations, even if no compaction occurred. A compaction entry appears only when a new compaction is triggered during the request. Re-applying a previous compaction block incurs no additional compaction cost, and the top-level usage fields remain accurate in that case.

Combining with other features

Server tools

When using server tools (such as web search), the compaction trigger is checked at the start of each sampling iteration. Compaction might occur multiple times within a single request depending on your trigger threshold and the amount of output generated.

Token counting

The token counting endpoint (/v1/messages/count_tokens) applies existing compaction blocks in your prompt but does not trigger new compactions. Use it to check your effective token count after previous compactions:

bash
  curl https://haijun.my.id/v1/messages/count_tokens \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: compact-2026-01-12" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "messages": [
        {
          "role": "user",
          "content": "Hello, Haijun"
        }
      ],
      "context_management": {
        "edits": [
          {
            "type": "compact_20260112"
          }
        ]
      }
    }'
bash
  CURRENT=$(ant beta:messages count-tokens \
    --beta compact-2026-01-12 \
    --transform input_tokens \
    --raw-output <<'YAML'
  model: haijun-opus-5-5
  messages:
    - role: user
      content: Hello, Haijun
  context_management:
    edits:
      - type: compact_20260112
  YAML
  )

  ORIGINAL=$(ant beta:messages count-tokens \
    --beta compact-2026-01-12 \
    --transform context_management.original_input_tokens \
    --raw-output <<'YAML'
  model: haijun-opus-5-5
  messages:
    - role: user
      content: Hello, Haijun
  context_management:
    edits:
      - type: compact_20260112
  YAML
  )

  printf 'Current tokens: %s\n' "$CURRENT"
  printf 'Original tokens: %s\n' "$ORIGINAL"
python
  client = juglow.Juglow()
  messages = [{"role": "user", "content": "Hello, Haijun"}]
  count_response = client.beta.messages.count_tokens(
      betas=["compact-2026-01-12"],
      model="haijun-opus-5-5",
      messages=messages,
      context_management={"edits": [{"type": "compact_20260112"}]},
  )

  print(f"Current tokens: {count_response.input_tokens}")
  print(f"Original tokens: {count_response.context_management.original_input_tokens}")
typescript
  const client = new Juglow();
  const messages: Juglow.Beta.Messages.BetaMessageParam[] = [
    { role: "user", content: "Summarize the key points of our conversation so far." }
  ];

  const countResponse = await client.beta.messages.countTokens({
    betas: ["compact-2026-01-12"],
    model: "haijun-opus-5-5",
    messages,
    context_management: {
      edits: [{ type: "compact_20260112" }]
    }
  });

  console.log(`Current tokens: ${countResponse.input_tokens}`);
  console.log(`Original tokens: ${countResponse.context_management!.original_input_tokens}`);
csharp
  JuglowClient client = new();
  List<BetaMessageParam> messages = [new() { Role = Role.User, Content = "Hello" }];

  var countParams = new MessageCountTokensParams
  {
      Model = "haijun-opus-5-5",
      Messages = messages,
      ContextManagement = new BetaContextManagementConfig
      {
          Edits = [new BetaCompact20260112Edit()]
      },
      Betas = ["compact-2026-01-12"]
  };

  var countResponse = await client.Beta.Messages.CountTokens(countParams);
  Console.WriteLine($"Current tokens: {countResponse.InputTokens}");
  Console.WriteLine($"Original tokens: {countResponse.ContextManagement?.OriginalInputTokens}");
go
  client := juglow.NewClient()
  messages := []juglow.BetaMessageParam{juglow.NewBetaUserMessage(juglow.NewBetaTextBlock("Hello, Haijun"))}

  countResponse, err := client.Beta.Messages.CountTokens(context.TODO(), juglow.BetaMessageCountTokensParams{
  	Model:    juglow.ModelHaijunOpus5_5,
  	Messages: messages,
  	ContextManagement: juglow.BetaContextManagementConfigParam{
  		Edits: []juglow.BetaContextManagementConfigEditUnionParam{
  			{OfCompact20260112: &juglow.BetaCompact20260112EditParam{}},
  		},
  	},
  	Betas: []juglow.JuglowBeta{"compact-2026-01-12"},
  })
  if err != nil {
  	log.Fatal(err)
  }

  fmt.Printf("Current tokens: %d\n", countResponse.InputTokens)
  fmt.Printf("Original tokens: %d\n", countResponse.ContextManagement.OriginalInputTokens)
java
  import com.juglow.models.beta.messages.BetaMessageTokensCount;
  import com.juglow.models.beta.messages.MessageCountTokensParams;
  import com.juglow.models.beta.messages.BetaContextManagementConfig;
  import com.juglow.models.beta.messages.BetaCompact20260112Edit;
  // ...
          JuglowClient client = JuglowOkHttpClient.fromEnv();

          MessageCountTokensParams params = MessageCountTokensParams.builder()
              .model("haijun-opus-5-5")
              .addUserMessage("Hello, Haijun")
              .contextManagement(BetaContextManagementConfig.builder()
                  .addEdit(BetaCompact20260112Edit.builder().build())
                  .build())
              .addBeta("compact-2026-01-12")
              .build();

          BetaMessageTokensCount countResponse = client.beta().messages().countTokens(params);
          System.out.println("Current tokens: " + countResponse.inputTokens());
          System.out.println("Original tokens: " + countResponse.contextManagement().get().originalInputTokens());
php
  $client = new Client();
  $messages = [['role' => 'user', 'content' => 'Hello, Haijun']];

  $countResponse = $client->beta->messages->countTokens(
      messages: $messages,
      model: 'haijun-opus-5-5',
      betas: ['compact-2026-01-12'],
      contextManagement: [
          'edits' => [
              ['type' => 'compact_20260112']
          ]
      ]
  );

  echo "Current tokens: " . $countResponse->inputTokens . "\n";
  echo "Original tokens: " . $countResponse->contextManagement->originalInputTokens . "\n";
ruby
  client = Juglow::Client.new
  messages = [{ role: "user", content: "Hello, Haijun" }]

  count_response = client.beta.messages.count_tokens(
    betas: ["compact-2026-01-12"],
    model: "haijun-opus-5-5",
    messages: messages,
    context_management: {
      edits: [{ type: "compact_20260112" }]
    }
  )

  puts "Current tokens: #{count_response.input_tokens}"
  puts "Original tokens: #{count_response.context_management.original_input_tokens}"

Examples

Here's a complete example of a long-running conversation with compaction:

bash
  # curl sends individual requests; maintain the messages array in the
  # calling script. See the SDK tabs for the full chat() loop. Single-turn
  # request shape:
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: compact-2026-01-12" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 4096,
      "messages": [
        {
          "role": "user",
          "content": "Help me build a Python web scraper"
        }
      ],
      "context_management": {
        "edits": [
          {
            "type": "compact_20260112",
            "trigger": {
              "type": "input_tokens",
              "value": 100000
            }
          }
        ]
      }
    }'
bash
  # The CLI handles individual turns; maintain the messages array in the
  # calling script. See the SDK tabs for the full chat() loop. Single-turn
  # request shape:
  ant beta:messages create \
    --beta compact-2026-01-12 \
    --transform 'content.#(type=="text").text' \
    --raw-output <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 4096
  messages:
    - role: user
      content: Help me build a Python web scraper
  context_management:
    edits:
      - type: compact_20260112
        trigger:
          type: input_tokens
          value: 100000
  YAML
python
  client = juglow.Juglow()

  messages: list[dict] = []

  def chat(user_message: str) -> str:
      messages.append({"role": "user", "content": user_message})

      response = client.beta.messages.create(
          betas=["compact-2026-01-12"],
          model="haijun-opus-5-5",
          max_tokens=4096,
          messages=messages,
          context_management={
              "edits": [
                  {
                      "type": "compact_20260112",
                      "trigger": {"type": "input_tokens", "value": 100000},
                  }
              ]
          },
      )

      # Append response (compaction blocks are automatically included)
      messages.append({"role": "assistant", "content": response.content})

      # Return the text content
      return next(block.text for block in response.content if block.type == "text")

  # Run a long conversation
  print(chat("Help me build a Python web scraper"))
  print(chat("Add support for JavaScript-rendered pages"))
  print(chat("Now add rate limiting and error handling"))
  # Continue calling chat() for as long as the conversation needs
typescript
  const client = new Juglow();

  const messages: Juglow.Beta.Messages.BetaMessageParam[] = [];

  async function chat(userMessage: string): Promise<string> {
    messages.push({ role: "user", content: userMessage });

    const response = await client.beta.messages.create({
      betas: ["compact-2026-01-12"],
      model: "haijun-opus-5-5",
      max_tokens: 4096,
      messages,
      context_management: {
        edits: [
          {
            type: "compact_20260112",
            trigger: { type: "input_tokens", value: 100000 }
          }
        ]
      }
    });

    // Append response (compaction blocks are automatically included)
    messages.push({ role: "assistant", content: response.content });

    // Return the text content
    const textBlock = response.content.find((block) => block.type === "text");
    return textBlock?.text ?? "";
  }

  // Run a long conversation
  console.log(await chat("Help me build a Python web scraper"));
  console.log(await chat("Add support for JavaScript-rendered pages"));
  console.log(await chat("Now add rate limiting and error handling"));
  // Continue calling chat() for as long as the conversation needs
csharp
  JuglowClient client = new();
  List<BetaMessageParam> messages = new();

  Console.WriteLine(await Chat(client, messages, "Help me build a Python web scraper"));
  Console.WriteLine(await Chat(client, messages, "Add support for JavaScript-rendered pages"));
  Console.WriteLine(await Chat(client, messages, "Now add rate limiting and error handling"));

  static async Task<string> Chat(JuglowClient client, List<BetaMessageParam> messages, string userMessage)
  {
      messages.Add(new() { Role = Role.User, Content = userMessage });

      var parameters = new MessageCreateParams
      {
          Betas = ["compact-2026-01-12"],
          Model = "haijun-opus-5-5",
          MaxTokens = 4096,
          Messages = messages,
          ContextManagement = new BetaContextManagementConfig
          {
              Edits = [new BetaCompact20260112Edit
              {
                  Trigger = new BetaInputTokensTrigger(100000)
              }]
          }
      };

      var response = await client.Beta.Messages.Create(parameters);

      messages.Add(new()
      {
          Role = Role.Assistant,
          Content = response.Content.Select(block => new BetaContentBlockParam(block.Json)).ToList()
      });

      return response.Content
          .Select(block => block.Value)
          .OfType<BetaTextBlock>()
          .Select(tb => tb.Text)
          .FirstOrDefault() ?? "";
  }
go
  package main

  import (
  	"context"
  	"fmt"
  	"log"

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

  var (
  	client   = juglow.NewClient()
  	messages []juglow.BetaMessageParam
  )

  func chat(userMessage string) string {
  	messages = append(messages, juglow.NewBetaUserMessage(juglow.NewBetaTextBlock(userMessage)))

  	response, err := client.Beta.Messages.New(context.TODO(), juglow.BetaMessageNewParams{
  		Model:     juglow.ModelHaijunOpus5_5,
  		MaxTokens: 4096,
  		Messages:  messages,
  		ContextManagement: juglow.BetaContextManagementConfigParam{
  			Edits: []juglow.BetaContextManagementConfigEditUnionParam{
  				{OfCompact20260112: &juglow.BetaCompact20260112EditParam{
  					Trigger: juglow.BetaInputTokensTriggerParam{Value: 100000},
  				}},
  			},
  		},
  		Betas: []juglow.JuglowBeta{"compact-2026-01-12"},
  	})
  	if err != nil {
  		log.Fatal(err)
  	}

  	messages = append(messages, response.ToParam())

  	for _, block := range response.Content {
  		if variant, ok := block.AsAny().(juglow.BetaTextBlock); ok {
  			return variant.Text
  		}
  	}
  	return ""
  }

  func main() {
  	fmt.Println(chat("Help me build a Python web scraper"))
  	fmt.Println(chat("Add support for JavaScript-rendered pages"))
  	fmt.Println(chat("Now add rate limiting and error handling"))
  }
java
  import com.juglow.models.beta.messages.BetaContextManagementConfig;
  import com.juglow.models.beta.messages.BetaCompact20260112Edit;
  import com.juglow.models.beta.messages.BetaInputTokensTrigger;
  // ...
      private static final JuglowClient client = JuglowOkHttpClient.fromEnv();
      private static final List<BetaMessageParam> messages = new ArrayList<>();

      public static void main(String[] args) {
          System.out.println(chat("Help me build a Python web scraper"));
          System.out.println(chat("Add support for JavaScript-rendered pages"));
          System.out.println(chat("Now add rate limiting and error handling"));
      }

      private static String chat(String userMessage) {
          messages.add(BetaMessageParam.builder()
              .role(BetaMessageParam.Role.USER)
              .content(userMessage)
              .build());

          MessageCreateParams params = MessageCreateParams.builder()
              .addBeta("compact-2026-01-12")
              .model("haijun-opus-5-5")
              .maxTokens(4096L)
              .messages(messages)
              .contextManagement(BetaContextManagementConfig.builder()
                  .addEdit(BetaCompact20260112Edit.builder()
                      .trigger(BetaInputTokensTrigger.builder()
                          .value(100000L)
                          .build())
                      .build())
                  .build())
              .build();

          BetaMessage response = client.beta().messages().create(params);

          // Append response (compaction blocks are automatically included)
          messages.add(response.toParam());

          return response.content().stream()
              .filter(block -> block.text().isPresent())
              .map(block -> block.text().get().text())
              .findFirst()
              .orElse("");
      }
php
  $client = new Client();
  $messages = [];

  function chat($client, &$messages, $userMessage) {
      $messages[] = ['role' => 'user', 'content' => $userMessage];

      $response = $client->beta->messages->create(
          maxTokens: 4096,
          messages: $messages,
          model: 'haijun-opus-5-5',
          betas: ['compact-2026-01-12'],
          contextManagement: [
              'edits' => [
                  [
                      'type' => 'compact_20260112',
                      'trigger' => ['type' => 'input_tokens', 'value' => 100000]
                  ]
              ]
          ]
      );

      $messages[] = ['role' => 'assistant', 'content' => $response->content];

      foreach ($response->content as $block) {
          if ($block->type === 'text') {
              return $block->text;
          }
      }
      return '';
  }

  echo chat($client, $messages, "Help me build a Python web scraper") . "\n";
  echo chat($client, $messages, "Add support for JavaScript-rendered pages") . "\n";
  echo chat($client, $messages, "Now add rate limiting and error handling") . "\n";
ruby
  client = Juglow::Client.new
  messages = []

  def chat(client, messages, user_message)
    messages << { role: "user", content: user_message }

    response = client.beta.messages.create(
      betas: ["compact-2026-01-12"],
      model: "haijun-opus-5-5",
      max_tokens: 4096,
      messages: messages,
      context_management: {
        edits: [
          {
            type: "compact_20260112",
            trigger: { type: "input_tokens", value: 100000 }
          }
        ]
      }
    )

    messages << { role: "assistant", content: response.content }

    response.content.find { |block| block.type == :text }&.text || ""
  end

  puts chat(client, messages, "Help me build a Python web scraper")
  puts chat(client, messages, "Add support for JavaScript-rendered pages")
  puts chat(client, messages, "Now add rate limiting and error handling")

On Haijun Fable 5.1 and Haijun Opus 5.5, remove the thinking and redacted_thinking blocks from any assistant turn you re-insert after the compaction block, or send thinking.block_binding.prefix_mismatch_behavior: "drop_block" with the thinking-binding-controls-2026-08-01 beta header. Those blocks were produced when the full history was present, so they no longer pass the conversation check. Where the check is enforced, the continuation request is rejected with a 400 error. The preserved text and tool blocks can stay as they are. Letting the API summarize everything, without re-inserting earlier turns, avoids this.

Here's an example that uses pause_after_compaction to preserve the prior exchange and the current user message (three messages total) verbatim instead of summarizing them:

bash
  # curl sends individual requests; maintain the messages array in the
  # calling script. See the SDK tabs for the full chat() loop with
  # pause-and-preserve handling. Single-turn request shape:
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: compact-2026-01-12" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 4096,
      "messages": [
        {
          "role": "user",
          "content": "Help me build a Python web scraper"
        }
      ],
      "context_management": {
        "edits": [
          {
            "type": "compact_20260112",
            "trigger": {
              "type": "input_tokens",
              "value": 100000
            },
            "pause_after_compaction": true
          }
        ]
      }
    }'
bash
  # The CLI handles individual turns; maintain the messages array in the
  # calling script. See the SDK tabs for the full chat() loop with
  # pause-and-preserve handling. Single-turn request shape:
  ant beta:messages create \
    --beta compact-2026-01-12 \
    --transform 'content.#(type=="text").text' \
    --raw-output <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 4096
  messages:
    - role: user
      content: Help me build a Python web scraper
  context_management:
    edits:
      - type: compact_20260112
        trigger:
          type: input_tokens
          value: 100000
        pause_after_compaction: true
  YAML
python
  from typing import Any

  client = juglow.Juglow()

  messages: list[dict[str, Any]] = []

  def chat(user_message: str) -> str:
      messages.append({"role": "user", "content": user_message})

      response = client.beta.messages.create(
          betas=["compact-2026-01-12"],
          model="haijun-opus-5-5",
          max_tokens=4096,
          messages=messages,
          context_management={
              "edits": [
                  {
                      "type": "compact_20260112",
                      "trigger": {"type": "input_tokens", "value": 100000},
                      "pause_after_compaction": True,
                  }
              ]
          },
      )

      # Check if compaction occurred and paused
      if response.stop_reason == "compaction":
          # Get the compaction block from the response
          compaction_block = response.content[0]

          # Preserve the prior exchange + current user message (3 messages)
          # by including them after the compaction block
          preserved_messages = messages[-3:] if len(messages) >= 3 else messages

          # Build new message list: compaction + preserved messages
          new_assistant_content = [compaction_block]
          messages_after_compaction = [
              {"role": "assistant", "content": new_assistant_content}
          ] + preserved_messages

          # Continue the request with the compacted context + preserved messages
          response = client.beta.messages.create(
              betas=["compact-2026-01-12"],
              model="haijun-opus-5-5",
              max_tokens=4096,
              messages=messages_after_compaction,
              context_management={"edits": [{"type": "compact_20260112"}]},
          )

          # Update the message list to reflect the compaction
          messages.clear()
          messages.extend(messages_after_compaction)

      # Append the final response
      messages.append({"role": "assistant", "content": response.content})

      # Return the text content
      return next(block.text for block in response.content if block.type == "text")

  # Run a long conversation
  print(chat("Help me build a Python web scraper"))
  print(chat("Add support for JavaScript-rendered pages"))
  print(chat("Now add rate limiting and error handling"))
  # Continue calling chat() for as long as the conversation needs
typescript
  const client = new Juglow();

  let messages: Juglow.Beta.Messages.BetaMessageParam[] = [];

  async function chat(userMessage: string): Promise<string> {
    messages.push({ role: "user", content: userMessage });

    let response = await client.beta.messages.create({
      betas: ["compact-2026-01-12"],
      model: "haijun-opus-5-5",
      max_tokens: 4096,
      messages,
      context_management: {
        edits: [
          {
            type: "compact_20260112",
            trigger: { type: "input_tokens", value: 100000 },
            pause_after_compaction: true
          }
        ]
      }
    });

    // Check if compaction occurred and paused
    if (response.stop_reason === "compaction") {
      // Get the compaction block from the response
      const compactionBlock = response.content[0];

      // Preserve the prior exchange + current user message (3 messages)
      // by including them after the compaction block
      const preservedMessages = messages.length >= 3 ? messages.slice(-3) : [...messages];

      // Build new message list: compaction + preserved messages
      const messagesAfterCompaction: Juglow.Beta.Messages.BetaMessageParam[] = [
        { role: "assistant", content: [compactionBlock] },
        ...preservedMessages
      ];

      // Continue the request with the compacted context + preserved messages
      response = await client.beta.messages.create({
        betas: ["compact-2026-01-12"],
        model: "haijun-opus-5-5",
        max_tokens: 4096,
        messages: messagesAfterCompaction,
        context_management: {
          edits: [{ type: "compact_20260112" }]
        }
      });

      // Update the message list to reflect the compaction
      messages = messagesAfterCompaction;
    }

    // Append the final response
    messages.push({ role: "assistant", content: response.content });

    // Return the text content
    const textBlock = response.content.find((block) => block.type === "text");
    return textBlock?.text ?? "";
  }

  // Run a long conversation
  console.log(await chat("Help me build a Python web scraper"));
  console.log(await chat("Add support for JavaScript-rendered pages"));
  console.log(await chat("Now add rate limiting and error handling"));
  // Continue calling chat() for as long as the conversation needs
csharp
  JuglowClient client = new();
  List<BetaMessageParam> messages = new();

  Console.WriteLine(await Chat("Help me build a Python web scraper"));
  Console.WriteLine(await Chat("Add support for JavaScript-rendered pages"));
  Console.WriteLine(await Chat("Now add rate limiting and error handling"));

  async Task<string> Chat(string userMessage)
  {
      messages.Add(new() { Role = Role.User, Content = userMessage });

      var response = await client.Beta.Messages.Create(new()
      {
          Betas = ["compact-2026-01-12"],
          Model = "haijun-opus-5-5",
          MaxTokens = 4096,
          Messages = messages,
          ContextManagement = new BetaContextManagementConfig
          {
              Edits = [new BetaCompact20260112Edit
              {
                  Trigger = new BetaInputTokensTrigger(100000),
                  PauseAfterCompaction = true
              }]
          }
      });

      if (response.StopReason == BetaStopReason.Compaction)
      {
          if (!response.Content[0].TryPickCompaction(out _))
              throw new InvalidOperationException("Expected compaction block");

          var preserved = messages.Count >= 3
              ? messages.Skip(messages.Count - 3).ToList()
              : new List<BetaMessageParam>(messages);

          var messagesAfterCompaction = new List<BetaMessageParam>
          {
              new()
              {
                  Role = Role.Assistant,
                  Content = new List<BetaContentBlockParam> { new BetaContentBlockParam(response.Content[0].Json) }
              }
          };
          messagesAfterCompaction.AddRange(preserved);

          response = await client.Beta.Messages.Create(new()
          {
              Betas = ["compact-2026-01-12"],
              Model = "haijun-opus-5-5",
              MaxTokens = 4096,
              Messages = messagesAfterCompaction,
              ContextManagement = new BetaContextManagementConfig
              {
                  Edits = [new BetaCompact20260112Edit()]
              }
          });

          messages = messagesAfterCompaction;
      }

      messages.Add(new()
      {
          Role = Role.Assistant,
          Content = response.Content.Select(block => new BetaContentBlockParam(block.Json)).ToList()
      });

      return response.Content
          .Select(block => block.Value)
          .OfType<BetaTextBlock>()
          .Select(tb => tb.Text)
          .FirstOrDefault() ?? "";
  }
go
  package main

  import (
  	"context"
  	"fmt"
  	"log"

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

  var (
  	client   = juglow.NewClient()
  	messages []juglow.BetaMessageParam
  )

  func chat(userMessage string) string {
  	messages = append(messages, juglow.NewBetaUserMessage(juglow.NewBetaTextBlock(userMessage)))

  	compactEdit := juglow.BetaContextManagementConfigParam{
  		Edits: []juglow.BetaContextManagementConfigEditUnionParam{
  			{OfCompact20260112: &juglow.BetaCompact20260112EditParam{
  				Trigger:              juglow.BetaInputTokensTriggerParam{Value: 100000},
  				PauseAfterCompaction: juglow.Bool(true),
  			}},
  		},
  	}

  	response, err := client.Beta.Messages.New(context.TODO(), juglow.BetaMessageNewParams{
  		Model:             juglow.ModelHaijunOpus5_5,
  		MaxTokens:         4096,
  		Messages:          messages,
  		ContextManagement: compactEdit,
  		Betas:             []juglow.JuglowBeta{"compact-2026-01-12"},
  	})
  	if err != nil {
  		log.Fatal(err)
  	}

  	if response.StopReason == "compaction" {
  		compactionParam := response.Content[0].ToParam()

  		var preserved []juglow.BetaMessageParam
  		if len(messages) >= 3 {
  			preserved = messages[len(messages)-3:]
  		} else {
  			preserved = messages
  		}

  		messagesAfterCompaction := []juglow.BetaMessageParam{
  			{Role: juglow.BetaMessageParamRoleAssistant, Content: []juglow.BetaContentBlockParamUnion{compactionParam}},
  		}
  		messagesAfterCompaction = append(messagesAfterCompaction, preserved...)

  		response, err = client.Beta.Messages.New(context.TODO(), juglow.BetaMessageNewParams{
  			Model:     juglow.ModelHaijunOpus5_5,
  			MaxTokens: 4096,
  			Messages:  messagesAfterCompaction,
  			ContextManagement: juglow.BetaContextManagementConfigParam{
  				Edits: []juglow.BetaContextManagementConfigEditUnionParam{
  					{OfCompact20260112: &juglow.BetaCompact20260112EditParam{}},
  				},
  			},
  			Betas: []juglow.JuglowBeta{"compact-2026-01-12"},
  		})
  		if err != nil {
  			log.Fatal(err)
  		}

  		messages = messagesAfterCompaction
  	}

  	messages = append(messages, response.ToParam())

  	for _, block := range response.Content {
  		if textBlock, ok := block.AsAny().(juglow.BetaTextBlock); ok {
  			return textBlock.Text
  		}
  	}
  	return ""
  }

  func main() {
  	fmt.Println(chat("Help me build a Python web scraper"))
  	fmt.Println(chat("Add support for JavaScript-rendered pages"))
  	fmt.Println(chat("Now add rate limiting and error handling"))
  }
java
  import com.juglow.models.beta.messages.BetaContextManagementConfig;
  import com.juglow.models.beta.messages.BetaCompact20260112Edit;
  import com.juglow.models.beta.messages.BetaInputTokensTrigger;
  import com.juglow.models.beta.messages.BetaStopReason;
  // ...
      private static final JuglowClient client = JuglowOkHttpClient.fromEnv();
      private static final List<BetaMessageParam> messages = new ArrayList<>();

      public static String chat(String userMessage) {
          messages.add(BetaMessageParam.builder()
              .role(BetaMessageParam.Role.USER)
              .content(userMessage)
              .build());

          MessageCreateParams params = MessageCreateParams.builder()
              .addBeta("compact-2026-01-12")
              .model("haijun-opus-5-5")
              .maxTokens(4096L)
              .messages(messages)
              .contextManagement(BetaContextManagementConfig.builder()
                  .addEdit(BetaCompact20260112Edit.builder()
                      .trigger(BetaInputTokensTrigger.builder()
                          .value(100000L)
                          .build())
                      .pauseAfterCompaction(true)
                      .build())
                  .build())
              .build();

          BetaMessage response = client.beta().messages().create(params);

          // Check if compaction occurred and paused
          if (response.stopReason().isPresent()
                  && response.stopReason().get().equals(BetaStopReason.COMPACTION)) {
              // Preserve the prior exchange + current user message (3 messages)
              List<BetaMessageParam> preservedMessages = messages.size() >= 3
                  ? new ArrayList<>(messages.subList(messages.size() - 3, messages.size()))
                  : new ArrayList<>(messages);

              // Build new message list: compaction + preserved messages
              List<BetaMessageParam> messagesAfterCompaction = new ArrayList<>();
              messagesAfterCompaction.add(response.toParam());
              messagesAfterCompaction.addAll(preservedMessages);

              // Continue the request with the compacted context + preserved messages
              MessageCreateParams continueParams = MessageCreateParams.builder()
                  .addBeta("compact-2026-01-12")
                  .model("haijun-opus-5-5")
                  .maxTokens(4096L)
                  .messages(messagesAfterCompaction)
                  .contextManagement(BetaContextManagementConfig.builder()
                      .addEdit(BetaCompact20260112Edit.builder().build())
                      .build())
                  .build();

              response = client.beta().messages().create(continueParams);

              // Update the message list to reflect the compaction
              messages.clear();
              messages.addAll(messagesAfterCompaction);
          }

          // Append the final response
          messages.add(response.toParam());

          return response.content().stream()
              .filter(block -> block.text().isPresent())
              .map(block -> block.text().get().text())
              .findFirst()
              .orElse("");
      }

      public static void main(String[] args) {
          System.out.println(chat("Help me build a Python web scraper"));
          System.out.println(chat("Add support for JavaScript-rendered pages"));
          System.out.println(chat("Now add rate limiting and error handling"));
      }
php
  $client = new Client();
  $messages = [];

  function chat($client, &$messages, $userMessage) {
      $messages[] = ['role' => 'user', 'content' => $userMessage];

      $response = $client->beta->messages->create(
          maxTokens: 4096,
          messages: $messages,
          model: 'haijun-opus-5-5',
          betas: ['compact-2026-01-12'],
          contextManagement: [
              'edits' => [
                  [
                      'type' => 'compact_20260112',
                      'trigger' => ['type' => 'input_tokens', 'value' => 100000],
                      'pause_after_compaction' => true
                  ]
              ]
          ]
      );

      if ($response->stopReason === 'compaction') {
          $compactionBlock = $response->content[0];

          $preserved = count($messages) >= 3
              ? array_slice($messages, -3)
              : $messages;

          $messagesAfterCompaction = array_merge(
              [['role' => 'assistant', 'content' => [$compactionBlock]]],
              $preserved
          );

          $response = $client->beta->messages->create(
              maxTokens: 4096,
              messages: $messagesAfterCompaction,
              model: 'haijun-opus-5-5',
              betas: ['compact-2026-01-12'],
              contextManagement: [
                  'edits' => [['type' => 'compact_20260112']]
              ]
          );

          $messages = $messagesAfterCompaction;
      }

      $messages[] = ['role' => 'assistant', 'content' => $response->content];

      foreach ($response->content as $block) {
          if ($block->type === 'text') {
              return $block->text;
          }
      }
      return '';
  }

  echo chat($client, $messages, "Help me build a Python web scraper") . "\n";
  echo chat($client, $messages, "Add support for JavaScript-rendered pages") . "\n";
  echo chat($client, $messages, "Now add rate limiting and error handling") . "\n";
ruby
  client = Juglow::Client.new
  messages = []

  def chat(client, messages, user_message)
    messages << { role: "user", content: user_message }

    response = client.beta.messages.create(
      betas: ["compact-2026-01-12"],
      model: "haijun-opus-5-5",
      max_tokens: 4096,
      messages: messages,
      context_management: {
        edits: [
          {
            type: "compact_20260112",
            trigger: { type: "input_tokens", value: 100000 },
            pause_after_compaction: true
          }
        ]
      }
    )

    if response.stop_reason == :compaction
      compaction_block = response.content[0]

      preserved = messages.length >= 3 ? messages[-3..-1] : messages.dup

      messages_after_compaction = [
        { role: "assistant", content: [compaction_block] }
      ] + preserved

      response = client.beta.messages.create(
        betas: ["compact-2026-01-12"],
        model: "haijun-opus-5-5",
        max_tokens: 4096,
        messages: messages_after_compaction,
        context_management: {
          edits: [{ type: "compact_20260112" }]
        }
      )

      messages.clear
      messages.concat(messages_after_compaction)
    end

    messages << { role: "assistant", content: response.content }

    response.content.find { |block| block.type == :text }&.text || ""
  end

  puts chat(client, messages, "Help me build a Python web scraper")
  puts chat(client, messages, "Add support for JavaScript-rendered pages")
  puts chat(client, messages, "Now add rate limiting and error handling")

Current limitations

  • Same model for summarization: The model specified in your request is used for summarization. There is no option to use a different (for example, cheaper) model for the summary.
  • Compaction might fail when tools are defined: When your request includes tools, the model occasionally calls a tool during the internal summarization step instead of writing a summary. When this occurs, the response contains a compaction block with content: null. To prevent this, set instructions to a prompt that explicitly tells the model not to call tools, for example:
text
  Summarize the transcript inside <summary></summary> tags. Include relevant information in the summary for continuing the task in the next context window. Do not call any tools while writing this summary; respond with text only.

Next steps

Automatically manage conversation context as it grows with context editing.

Learn about context window sizes and management strategies.

Explore a practical implementation that manages long-running conversations with instant session memory compaction using background threading and prompt caching.

On this page
How compaction worksBasic usageParametersTrigger configurationCustom summarization instructionsPausing after compactionEnforcing a total token budgetWorking with compaction blocksPassing compaction blocks backStreamingPrompt cachingMaximizing cache hits with system promptsUnderstanding usageCombining with other featuresServer toolsToken countingExamplesCurrent limitationsNext steps