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
-
compactCompletedToolOutputs — Replaces stale completed tool outputs with a short marker (preserving last two tool results per call chain)
-
collapseRepeatedScaffolds — Detects identical consecutive slash-command scaffold texts and collapses all but the newest into an omission marker
-
collapseRepeatedErrorLoops — Detects identical consecutive provider error tool results and collapses all but the newest
-
collapseOlderTodoSnapshots — Detects consecutive todowrite snapshots and preserves only the newest
-
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:
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).
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 — afterstripStaleMetadata()and beforesaveContext. 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 configurablemaxPayloadBytesthreshold:Compaction Passes
compactCompletedToolOutputs— Replaces stale completed tool outputs with a short marker (preserving last two tool results per call chain)collapseRepeatedScaffolds— Detects identical consecutive slash-command scaffold texts and collapses all but the newest into an omission markercollapseRepeatedErrorLoops— Detects identical consecutive provider error tool results and collapses all but the newestcollapseOlderTodoSnapshots— Detects consecutivetodowritesnapshots and preserves only the newestremoveOldNonProtectedMessages— Last resort: removes old non-protected messages from oldest first until budget is metProtected Messages (never removed)
[Compressed conversation section]placeholdersrole === "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:Safety Margin Derivation
hardLimitBytesreserveBytessafetyMarginBytessafeClampTargetBytesWhen
maxPayloadBytes > hardLimitBytes, the gate silently caps tosafeClampTargetBytesand logs a warning.Telemetry
The return object provides:
startingBytes/endingBytes— Pre/post payload byte countschanged— Whether compaction occurredreductionPasses— Array of pass names that contributedaffectedMessageRefs/affectedCallIds— Affected message and call identifiersfailClosedReason— Non-null only when fail-closeddiagnostics— Human-readable summaryTest Results
Files Changed
src/lib/messages/byte-budget.tspruneByByteBudget(),measureMessagePayloadBytes(), 5 compaction passessrc/lib/messages/byte-budget.d.tssrc/lib/hooks.tscreateChatMessageTransformHandler()callspruneByByteBudget()as final transform stepsrc/lib/hooks.d.tssrc/lib/config.tscompress.maxPayloadBytesto valid config keys and type validationsrc/lib/config.d.tsmaxPayloadBytesPrior Art / Related
This feature is complementary to (and independent of) the bounded-range archive mode (PR #501). The two features address different dimensions:
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).