Skip to content

Feature Request: Byte-budget payload pruning gate #531

@EZotoff

Description

@EZotoff

Problem

Long-running DCP sessions can accumulate tool outputs, repeated scaffold instructions, error loops, and todo snapshots that bloat the serialized message payload past the 2 MiB (2,097,152-byte) protocol limit enforced by model providers. When the payload exceeds this limit, providers reject the request with a 413 Payload Too Large error, causing an unrecoverable session failure.

DCP's existing compression strategies (deduplication, error purging, range compression) do not bound the total raw byte size of the message payload. There is no gate at the end of the chat transform pipeline that enforces a hard byte cap.

Proposed Solution

Add a byte-budget enforcement gate at the end of DCP's createChatMessageTransformHandler() pipeline — after stripStaleMetadata() and before saveContext. The gate measures the full UTF-8 byte size of the serialized message list and applies up to five deterministic compaction passes when the payload exceeds a configurable maxPayloadBytes threshold:

Compaction Passes

  1. compactCompletedToolOutputs — Replaces stale completed tool outputs with a short marker (preserving last two tool results per call chain)

  2. collapseRepeatedScaffolds — Detects identical consecutive slash-command scaffold texts and collapses all but the newest into an omission marker

  3. collapseRepeatedErrorLoops — Detects identical consecutive provider error tool results and collapses all but the newest

  4. collapseOlderTodoSnapshots — Detects consecutive todowrite snapshots and preserves only the newest

  5. removeOldNonProtectedMessages — Last resort: removes old non-protected messages from oldest first until budget is met

Protected Messages (never removed)

  • The frontier — last user message and its assistant response
  • Messages containing [Compressed conversation section] placeholders
  • Messages with role === "user"

Fail-Closed Behavior

If the protected frontier alone exceeds maxPayloadBytes, the gate does not remove messages. It logs a diagnostic warning: "protected frontier exceeds maxPayloadBytes" and leaves the payload unchanged.

Configuration

New optional key under compress:

"compress": {
    // Byte-aware pruning cap.
    // Default: 1,802,240 bytes (2 MiB hard limit - 262,144 reserve - 32,768 safety margin)
    "maxPayloadBytes": 1802240
}

Safety Margin Derivation

Parameter Value Rationale
hardLimitBytes 2,097,152 2 MiB protocol limit
reserveBytes 262,144 Headroom for injected tool outputs and nudge overhead
safetyMarginBytes 32,768 Conservative operating envelope
safeClampTargetBytes 1,802,240 Effective clamp target

When maxPayloadBytes > hardLimitBytes, the gate silently caps to safeClampTargetBytes and logs a warning.

Telemetry

The return object provides:

  • startingBytes / endingBytes — Pre/post payload byte counts
  • changed — Whether compaction occurred
  • reductionPasses — Array of pass names that contributed
  • affectedMessageRefs / affectedCallIds — Affected message and call identifiers
  • failClosedReason — Non-null only when fail-closed
  • diagnostics — Human-readable summary

Test Results

  • 12/12 installed tests pass (3 marker checks across three DCP install copies + 9 functional regression cases)
  • 15/15 source-dist tests pass (including multibyte encoding, threshold boundary, and protected-failover edge cases)

Files Changed

File Status Purpose
src/lib/messages/byte-budget.ts New Core module: pruneByByteBudget(), measureMessagePayloadBytes(), 5 compaction passes
src/lib/messages/byte-budget.d.ts New Type declarations
src/lib/hooks.ts Modified createChatMessageTransformHandler() calls pruneByByteBudget() as final transform step
src/lib/hooks.d.ts Modified Type declarations for modified hooks
src/lib/config.ts Modified Added compress.maxPayloadBytes to valid config keys and type validation
src/lib/config.d.ts Modified Type declaration for maxPayloadBytes

Prior Art / Related

This feature is complementary to (and independent of) the bounded-range archive mode (PR #501). The two features address different dimensions:

  • Bounded archive mode: Controls token budget for structured archive summaries
  • Byte-budget gate: Enforces hard byte cap on the serialized message payload

Both patches are currently maintained as local modifications in https://github.com/EZotoff/ez-omo-config and can be deprecated in favor of upstream implementations.

Request

Please review for potential upstream merge. Happy to adapt the implementation to fit project conventions, refactor the compaction passes, adjust the configuration schema, or split into smaller incremental PRs.


This issue was generated from a working implementation with full test coverage. The local patch ID is opencode-dcp--byte-budget (dependency: @tarquinen/opencode-dcp@3.1.9).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions