Skip to content
Open
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
11 changes: 8 additions & 3 deletions apps/web/src/components/ChatView.logic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1094,16 +1094,21 @@ describe("planComposerTurn", () => {
});
});

it("queues behind a compaction of a stale session, except for /compact itself", () => {
it("queues behind a compaction of a stale session, except for /compact and /clear", () => {
const stale = { ...base, resumeCompactionTokens: 120_000 };
for (const messageText of ["hello", "/compactfoo"]) {
for (const messageText of ["hello", "/compactfoo", "/clearly not a command"]) {
expect(planComposerTurn({ ...stale, messageText })).toMatchObject({
compactBeforeSend: true,
turnDispatchMode: "queue",
shouldQueueBehindActiveRun: true,
});
}
for (const messageText of ["/COMPACT", "/compact keep the API notes", "/compact\n tests"]) {
for (const messageText of [
"/COMPACT",
"/compact keep the API notes",
"/compact\n tests",
"/clear",
]) {
expect(planComposerTurn({ ...stale, messageText })).toMatchObject({
compactBeforeSend: false,
turnDispatchMode: "auto",
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/components/ChatView.logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
deriveThreadRuntime,
} from "@t3tools/client-runtime/state/thread-execution";
import { checkpointFileTotals } from "@t3tools/client-runtime/state/thread-checkpoints";
import { COMPACT_COMMAND_PATTERN } from "@t3tools/client-runtime/state/context-window";
import { NO_COMPACT_BEFORE_COMMAND_PATTERN } from "@t3tools/client-runtime/state/context-window";
import { derivePendingBackgroundWork } from "@t3tools/shared/orchestrationV2PendingBackgroundWork";
import {
latestUnheldRun,
Expand Down Expand Up @@ -1319,7 +1319,7 @@ export function planComposerTurn(input: {
!input.freshContext &&
input.resumeCompactionTokens !== null &&
!input.keepFullHistory &&
!COMPACT_COMMAND_PATTERN.test(input.messageText);
!NO_COMPACT_BEFORE_COMMAND_PATTERN.test(input.messageText);
const dispatchMode =
input.freshContext && (input.dispatchMode === "steer" || input.dispatchMode === "restart")
? "queue"
Expand Down
1 change: 1 addition & 0 deletions packages/client-runtime/src/state/contextWindow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ describe("shouldCompactBeforeSend", () => {
{ name: "while the thread is working", threadIdle: false, expected: false },
{ name: "with no conversation but /compact", onlyCompactMessages: true, expected: false },
{ name: "for a typed /compact", text: "/compact keep the plan", expected: false },
{ name: "for a typed /clear", text: "/clear", expected: false },
] as const)("$name", ({ name: _name, expected, ...overrides }) => {
expect(shouldCompactBeforeSend(sendInput(overrides as Parameters<typeof sendInput>[0]))).toBe(
expected,
Expand Down
7 changes: 4 additions & 3 deletions packages/client-runtime/src/state/contextWindow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ import type { ThreadStructure, ThreadStructureTurnItem } from "./models.ts";
const CLAUDE_RESUME_COMPACTION_FALLBACK_MS = 70 * 60_000;

// A typed `/compact`, with or without instructions, is already the compaction. Queueing a
// bare one ahead of it would run an uninstructed compaction first and charge for two.
export const COMPACT_COMMAND_PATTERN = /^\/compact(?:\s|$)/i;
// bare one ahead of it would run an uninstructed compaction first and charge for two. A typed
// `/clear` discards the old context, so compacting it first pays for a summary nobody reads.
export const NO_COMPACT_BEFORE_COMMAND_PATTERN = /^\/(?:compact|clear)(?:\s|$)/i;

function asFiniteNumber(value: unknown): number | null {
return typeof value === "number" && Number.isFinite(value) ? value : null;
Expand Down Expand Up @@ -265,7 +266,7 @@ export function shouldCompactBeforeSend(input: {
if (
provider === undefined ||
!input.threadIdle ||
COMPACT_COMMAND_PATTERN.test(input.text) ||
NO_COMPACT_BEFORE_COMMAND_PATTERN.test(input.text) ||
resolveIdleCompaction(input.settings, provider.instanceId) === "off" ||
!provider.slashCommands.some((command) => command.name === "compact") ||
resolvePendingHandoff(projection, input.selection, input.providers) !== null ||
Expand Down
Loading