Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1810,18 +1810,22 @@ describe("CodexAppServerAgent", () => {
} as unknown as NewSessionRequest);

// The compaction item brackets it: started → in progress, completed → boundary.
stub.emit("thread/tokenUsage/updated", {
tokenUsage: { last: { inputTokens: 90000, totalTokens: 120000 } },
});
stub.emit("item/started", {
item: { type: "contextCompaction", id: "c1" },
});
stub.emit("item/completed", {
item: { type: "contextCompaction", id: "c1", summary: "…" },
});

// compact_boundary clears isCompacting + drains the host queue.
// compact_boundary clears isCompacting + drains the host queue; trigger/preTokens
// feed the renderer's boundary marker.
expect(
extNotifications.find((n) => n.method === "_posthog/compact_boundary")
?.params,
).toMatchObject({ sessionId: "t" });
).toMatchObject({ sessionId: "t", trigger: "auto", preTokens: 120000 });
// ...and a user-visible marker lands in the transcript.
expect(sessionUpdates).toContainEqual({
sessionId: "t",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,10 @@ export class CodexAppServerAgent extends BaseAcpAgent {
void this.client
.extNotification(POSTHOG_NOTIFICATIONS.COMPACT_BOUNDARY, {
sessionId: this.sessionId,
// codex only compacts on its own (contextCompaction items); preTokens is the last
// context occupancy reading, omitted when no usage arrived yet.
trigger: "auto",
preTokens: this.usage.contextTokens(),
})
.catch(() => undefined);
void this.client
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -541,9 +541,10 @@ function handleNotification(

if (isNotification(msg.method, POSTHOG_NOTIFICATIONS.COMPACT_BOUNDARY)) {
ensureImplicitTurn(b, ts);
// trigger/preTokens are adapter-dependent: the claude adapter sends both, codex sends neither.
const params = msg.params as {
trigger: "manual" | "auto";
preTokens: number;
trigger?: "manual" | "auto";
preTokens?: number;
contextSize?: number;
};
markCompactingStatusComplete(b);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { Badge, Box, Flex, Text } from "@radix-ui/themes";
import { useChatThreadChrome } from "../chat-thread/chatThreadChrome";

interface CompactBoundaryViewProps {
trigger: "manual" | "auto";
preTokens: number;
// Both optional — the codex adapter reports a boundary without metadata.
trigger?: "manual" | "auto";
preTokens?: number;
contextSize?: number;
}

Expand All @@ -14,23 +15,24 @@ export function CompactBoundaryView({
preTokens,
contextSize,
}: CompactBoundaryViewProps) {
const tokensK = Math.round(preTokens / 1000);
const hasTokens = typeof preTokens === "number" && Number.isFinite(preTokens);
const tokensK = hasTokens ? Math.round(preTokens / 1000) : null;
const percent =
contextSize && contextSize > 0
hasTokens && contextSize && contextSize > 0
? Math.round((preTokens / contextSize) * 100)
: null;
// New thread renders the boundary as a centered separator marker; the legacy thread keeps its
// bordered badge row so ConversationView is unchanged when the chat thread is off.
const chatChrome = useChatThreadChrome();

if (chatChrome) {
const markerParts = ["Conversation compacted"];
if (trigger) markerParts.push(trigger);
if (percent !== null) markerParts.push(`${percent}% of context`);
else if (tokensK !== null) markerParts.push(`~${tokensK}K tokens`);
return (
<ChatMarker variant="separator">
<ChatMarkerContent>
{`Conversation compacted · ${trigger} · ${
percent !== null ? `${percent}% of context` : `~${tokensK}K tokens`
}`}
</ChatMarkerContent>
<ChatMarkerContent>{markerParts.join(" · ")}</ChatMarkerContent>
</ChatMarker>
);
}
Expand All @@ -40,18 +42,22 @@ export function CompactBoundaryView({
<Flex align="center" gap="2">
<Lightning size={14} weight="fill" className="text-blue-9" />
<Text className="text-[13px] text-gray-11">Conversation compacted</Text>
<Badge
size="1"
color={trigger === "auto" ? "orange" : "blue"}
variant="soft"
>
{trigger}
</Badge>
<Text className="text-[13px] text-gray-9">
{percent !== null
? `(${percent}% of context · ~${tokensK}K tokens summarized)`
: `(~${tokensK}K tokens summarized)`}
</Text>
{trigger && (
<Badge
size="1"
color={trigger === "auto" ? "orange" : "blue"}
variant="soft"
>
{trigger}
</Badge>
)}
{tokensK !== null && (
<Text className="text-[13px] text-gray-9">
{percent !== null
? `(${percent}% of context · ~${tokensK}K tokens summarized)`
: `(~${tokensK}K tokens summarized)`}
</Text>
)}
</Flex>
</Box>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export type RenderItem =
}
| {
sessionUpdate: "compact_boundary";
trigger: "manual" | "auto";
preTokens: number;
trigger?: "manual" | "auto";
preTokens?: number;
contextSize?: number;
}
| {
Expand Down
Loading