Skip to content
Merged
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
1 change: 1 addition & 0 deletions mobile/src/utils/slashCommandHelpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ describe("buildMobileCompactionPayload", () => {
const payload = buildMobileCompactionPayload(parsed, baseOptions);

expect(payload.messageText).toContain("approximately 615 words");

expect(payload.messageText).toContain(parsed.continueMessage);
expect(payload.metadata.type).toBe("compaction-request");
expect(payload.metadata.rawCommand).toContain("/compact -t 800 -m anthropic:claude-opus-4-1");
Expand Down
15 changes: 8 additions & 7 deletions mobile/src/utils/slashCommandHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ import type { MuxFrontendMetadata } from "@/common/types/message";
import type { ParsedCommand, SlashSuggestion } from "@/browser/utils/slashCommands/types";
import type { InferClientInputs } from "@orpc/client";
import type { ORPCClient } from "../orpc/client";
import {
DEFAULT_COMPACTION_WORD_TARGET,
WORDS_TO_TOKENS_RATIO,
buildCompactionPrompt,
} from "@/common/constants/ui";

type SendMessageOptions = NonNullable<
InferClientInputs<ORPCClient>["workspace"]["sendMessage"]["options"]
>;

export const MOBILE_HIDDEN_COMMANDS = new Set(["telemetry", "vim"]);
const WORDS_PER_TOKEN = 1.3;
const DEFAULT_WORD_TARGET = 2000;

export function extractRootCommand(replacement: string): string | null {
if (typeof replacement !== "string") {
Expand Down Expand Up @@ -44,12 +47,10 @@ export function buildMobileCompactionPayload(
baseOptions: SendMessageOptions
): MobileCompactionPayload {
const targetWords = parsed.maxOutputTokens
? Math.round(parsed.maxOutputTokens / WORDS_PER_TOKEN)
: DEFAULT_WORD_TARGET;
? Math.round(parsed.maxOutputTokens / WORDS_TO_TOKENS_RATIO)
: DEFAULT_COMPACTION_WORD_TARGET;

let messageText =
`Summarize this conversation into a compact form for a new Assistant to continue helping the user. ` +
`Use approximately ${targetWords} words.`;
let messageText = buildCompactionPrompt(targetWords);

if (parsed.continueMessage) {
messageText += `\n\nThe user wants to continue with: ${parsed.continueMessage}`;
Expand Down
12 changes: 0 additions & 12 deletions src/browser/utils/chatCommands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,18 +128,6 @@ describe("prepareCompactionMessage", () => {
expect(metadata.parsed.continueMessage?.model).toBe(sendMessageOptions.model);
});

test("generates correct prompt text with strict summary instructions", () => {
const sendMessageOptions = createBaseOptions();
const { messageText } = prepareCompactionMessage({
workspaceId: "ws-1",
maxOutputTokens: 4096,
sendMessageOptions,
});

expect(messageText).toContain("Focus entirely on the summary");
expect(messageText).toContain("Do not suggest next steps or future actions");
});

test("does not create continueMessage when no text or images provided", () => {
const sendMessageOptions = createBaseOptions();
const { metadata } = prepareCompactionMessage({
Expand Down
8 changes: 6 additions & 2 deletions src/browser/utils/chatCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ import { resolveCompactionModel } from "@/browser/utils/messages/compactionModel
import type { ImageAttachment } from "../components/ImageAttachments";
import { dispatchWorkspaceSwitch } from "./workspaceEvents";
import { getRuntimeKey, copyWorkspaceStorage } from "@/common/constants/storage";
import { DEFAULT_COMPACTION_WORD_TARGET, WORDS_TO_TOKENS_RATIO } from "@/common/constants/ui";
import {
DEFAULT_COMPACTION_WORD_TARGET,
WORDS_TO_TOKENS_RATIO,
buildCompactionPrompt,
} from "@/common/constants/ui";

// ============================================================================
// Workspace Creation
Expand Down Expand Up @@ -593,7 +597,7 @@ export function prepareCompactionMessage(options: CompactionOptions): {
: DEFAULT_COMPACTION_WORD_TARGET;

// Build compaction message with optional continue context
let messageText = `Summarize this conversation into a compact form for a new Assistant to continue helping the user. Focus entirely on the summary of what has happened. Do not suggest next steps or future actions. Use approximately ${targetWords} words.`;
let messageText = buildCompactionPrompt(targetWords);

if (options.continueMessage) {
messageText += `\n\nThe user wants to continue with: ${options.continueMessage.text}`;
Expand Down
25 changes: 25 additions & 0 deletions src/common/constants/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,31 @@ export const DEFAULT_COMPACTION_WORD_TARGET = 2000;
*/
export const WORDS_TO_TOKENS_RATIO = 1.3;

/**
* Build the compaction prompt for a given word target.
* Shared across desktop and mobile clients.
*/
export function buildCompactionPrompt(targetWords: number): string {
return `Summarize this conversation for a new Assistant to continue helping the user.

Your summary must be approximately ${targetWords} words.

Include:
- The user's overall goal and current task
- Key decisions made and their rationale
- Current state of the work (what's done, what's in progress)
- Important technical details (file paths, function names, configurations)
- Any errors encountered and how they were resolved
- Unresolved issues or blockers

Do not include:
- Suggestions for next steps
- Conversational filler or pleasantries
- Redundant information

Write in a factual, dense style. Every sentence should convey essential context.`;
}

/**
* Force-compact this many percentage points after threshold.
* Gives user a buffer zone between warning and force-compaction.
Expand Down
4 changes: 2 additions & 2 deletions src/node/services/mock/scenarios/slashCommands.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type { ScenarioTurn } from "@/node/services/mock/scenarioTypes";
import { KNOWN_MODELS } from "@/common/constants/knownModels";
import { STREAM_BASE_DELAY } from "@/node/services/mock/scenarioTypes";
import { buildCompactionPrompt } from "@/common/constants/ui";

export const SLASH_COMMAND_PROMPTS = {
MODEL_STATUS: "Please confirm which model is currently active for this conversation.",
} as const;

export const COMPACTION_MESSAGE =
"Summarize this conversation into a compact form for a new Assistant to continue helping the user. Focus entirely on the summary of what has happened. Do not suggest next steps or future actions. Use approximately 385 words.";
export const COMPACTION_MESSAGE = buildCompactionPrompt(385);

export const COMPACT_SUMMARY_TEXT =
"Compact summary: The assistant read project files, listed directory contents, created and inspected test.txt, then confirmed the contents remained 'hello'. Technical details preserved.";
Expand Down