Skip to content

prune: summary anchor can land mid-conversation (role:system after a user message) — 400 on strict OpenAI backends #170

Description

@ranxianglei

What

collectSummaryAnchors (src/prune.ts, dist/index.js:136-165) sets insertAt = earliest ?? 0 where earliest is the index of the block's earliest covered message in the input messages. rebuildMessages (dist/index.js:166-189) then inserts the rendered summary — renderSummary produces role: "system" (dist/index.js:190-201) — at that index.

Whenever the covered range does NOT start at the conversation head, the anchor sits after one or more still-visible messages, and the rebuilt message list carries a system message mid-conversation:

[user(m00001), assistant(m00002), system(summary), user(m00011), ...]

This is not limited to multi-segment compress: any compress whose range starts after the head (the normal "fold the old stuff, keep the tail" pattern) produces it. The only safe shape is a range starting at index 0.

Note the kernel already preserves the first user message unconditionally (rebuildMessages, dist/index.js:176-179: if (index === firstUserIndex && firstUserIndex >= 0) { result.push(...); continue; }), so even a head-anchored range m00001–mNNN leaves m00001 visible — the summary lands at index 0, ahead of it, which is fine. The problem is exclusively anchors at index > 0.

Impact

Suggested fix

Clamp the anchor to the first user message in collectSummaryAnchorsfirstUserIndex is already computed in prune() (dist/index.js:117-119) but only passed to rebuildMessages:

insertAt: Math.min(earliest ?? 0, firstUserIndex)

This folds every summary into the leading system prefix. Semantics are unchanged: a summary is a stand-in for the folded history, and a leading system prefix is accepted by both OpenAI and Anthropic (the anthropic wire already maps non-assistant roles to user, so it is unaffected today).

Alternative considered: render summaries as role: "user" — works everywhere but changes role semantics for every host.

Interim mitigation

billion-context PR ranxianglei/billion-context#356 hoists mid-conversation system/developer messages to the leading prefix at the OpenAI wire layer (both prepareOpenai and the compress-loop adapter), so proxy users are covered without a kernel release. This issue tracks the kernel-level root fix so all hosts benefit.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions