compaction: the pure core — cut walk, marker, and the model-facing projection (round 14 slice 1)
- core/compaction.ts: compactionCutIndex (backwards ~20k-token walk, NEVER separates a tool result from its call — the cut extends to the call's assistant message; a pending pose always survives), estimateTokens (chars/4, pi's method), newCompactionMarker (header + summary; details carry firstKeptMessageId/tokensBefore/model/at), serializeForSummary (labeled text, tool results truncated ~2k), planTreeToText (verbatim tree with transcript-derived statuses), buildCompactionPrompt (the four ruled sections: learner understanding + evolution, teaching arc, plan tree verbatim, current moment) - toLlmMessages honors the cut: the ACTIVE (newest) marker renders ONCE as the head; everything before firstKeptMessageId drops; corrupt cut degrades honestly (everything renders, never throw) - constants: KEEP_RECENT 20k, RESERVE 16k, DEFAULT_THRESHOLD 150k - 16 test pins
This commit is contained in:
@@ -42,6 +42,7 @@ import {
|
||||
THINKING_LABEL,
|
||||
} from "./contracts/chat.ts";
|
||||
import { compactAnswerForModel, type QuestionAnswerData } from "./question-core.ts";
|
||||
import { activeCompactionOf, keptIndexFor } from "./compaction.ts";
|
||||
|
||||
/** The hard-invariant wording for an aborted turn's unanswered calls —
|
||||
* carried over verbatim from the pre-native era so the model's view
|
||||
@@ -70,7 +71,8 @@ export interface LhMarkerMessage {
|
||||
| "subagent-abort"
|
||||
| "plan-block"
|
||||
| "practice-thinking"
|
||||
| "problem-dismissed";
|
||||
| "problem-dismissed"
|
||||
| "compaction";
|
||||
/** The model-facing text ("" for plan blocks — UI-only). */
|
||||
content: string;
|
||||
display: boolean;
|
||||
@@ -468,8 +470,29 @@ function resultIdsFrom(messages: readonly AgentMessage[]): Set<string> {
|
||||
* through untouched rather than killing the loop.
|
||||
*/
|
||||
export function toLlmMessages(messages: readonly AgentMessage[]): Message[] {
|
||||
const out: Message[] = [];
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
// THE COMPACTION CUT (owner round 14, 2026-10-05): the ACTIVE marker
|
||||
// (the newest — chained compactions rule last) names the first kept
|
||||
// message; everything before it is replaced by the marker's content
|
||||
// (header + summary) rendered ONCE, up top. The marker itself is then
|
||||
// skipped in place — it must never render twice. A cut naming a
|
||||
// missing id degrades honestly (everything renders — never throw).
|
||||
const lhMessages = messages as readonly LhMessage[];
|
||||
const active = activeCompactionOf(lhMessages);
|
||||
let start = 0;
|
||||
let head: Message | null = null;
|
||||
if (active) {
|
||||
const kept = keptIndexFor(lhMessages, active);
|
||||
if (kept > 0) {
|
||||
start = kept;
|
||||
head = {
|
||||
role: "user",
|
||||
content: [{ type: "text", text: active.content }],
|
||||
timestamp: active.timestamp,
|
||||
};
|
||||
}
|
||||
}
|
||||
const out: Message[] = head ? [head] : [];
|
||||
for (let i = start; i < messages.length; i++) {
|
||||
const m = messages[i]!;
|
||||
const role = (m as { role?: string }).role;
|
||||
if (role === "custom") {
|
||||
@@ -477,10 +500,15 @@ export function toLlmMessages(messages: readonly AgentMessage[]): Message[] {
|
||||
// UI-only markers: plan blocks, and problem dismissals (the
|
||||
// synthesized conclude pair carries the model's face).
|
||||
if (marker.customType === "plan-block" || marker.customType === "problem-dismissed") continue;
|
||||
// The ACTIVE compaction marker already rendered as the head —
|
||||
// never twice. (Stale markers before the cut are unreachable; a
|
||||
// marker whose cut could not resolve has no head and renders as
|
||||
// its user text — honest degradation.)
|
||||
if (marker.customType === "compaction" && head !== null && marker.id === active!.id) continue;
|
||||
out.push({
|
||||
role: "user",
|
||||
content: [{ type: "text", text: marker.content }],
|
||||
timestamp: marker.timestamp,
|
||||
timestamp: marker.timestamp,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user