MCPcopy
hub / github.com/coder/mux / performCompaction

Method performCompaction

src/node/services/compactionHandler.ts:1009–1188  ·  view source on GitHub ↗

* Perform history compaction by persisting a durable summary boundary. * * Steps: * 1. Delete partial state to avoid stale partial replay * 2. Persist post-compaction attachment state * 3. Prefer updating the streamed summary in-place, otherwise append a fallback summary * 4. Emit

(
    summary: string,
    metadata: {
      model: string;
      usage?: LanguageModelV2Usage;
      contextUsage?: LanguageModelV2Usage;
      duration?: number;
      providerMetadata?: Record<string, unknown>;
      contextProviderMetadata?: Record<string, unknown>;
      systemMessageTokens?: number;
    },
    messages: MuxMessage[],
    streamedSummaryMessageId: string,
    compactionRequestMessageId: string,
    isIdleCompaction = false,
    pendingFollowUp?: CompactionFollowUpRequest
  )

Source from the content-addressed store, hash-verified

1007 * 4. Emit summary message to frontend
1008 */
1009 private async performCompaction(
1010 summary: string,
1011 metadata: {
1012 model: string;
1013 usage?: LanguageModelV2Usage;
1014 contextUsage?: LanguageModelV2Usage;
1015 duration?: number;
1016 providerMetadata?: Record<string, unknown>;
1017 contextProviderMetadata?: Record<string, unknown>;
1018 systemMessageTokens?: number;
1019 },
1020 messages: MuxMessage[],
1021 streamedSummaryMessageId: string,
1022 compactionRequestMessageId: string,
1023 isIdleCompaction = false,
1024 pendingFollowUp?: CompactionFollowUpRequest
1025 ): Promise<Result<CompactionCompletionMetadata, string>> {
1026 assert(summary.trim().length > 0, "performCompaction requires a non-empty summary");
1027 assert(metadata.model.trim().length > 0, "Compaction summary requires a model");
1028 assert(
1029 streamedSummaryMessageId.trim().length > 0,
1030 "performCompaction requires streamed summary message ID"
1031 );
1032
1033 // CRITICAL: Delete partial.json BEFORE persisting compaction summary.
1034 // This prevents a race condition where:
1035 // 1. CompactionHandler persists summary
1036 // 2. sendQueuedMessages triggers commitPartial
1037 // 3. commitPartial finds stale partial.json and appends it to history
1038 // By deleting partial first, commitPartial becomes a no-op
1039 const deletePartialResult = await this.historyService.deletePartial(this.workspaceId);
1040 if (!deletePartialResult.success) {
1041 log.warn(`Failed to delete partial before compaction: ${deletePartialResult.error}`);
1042 // Continue anyway - the partial may not exist, which is fine
1043 }
1044
1045 // Extract diffs from the latest compaction epoch only, so append-only history
1046 // does not re-inject stale pre-boundary edits after subsequent compactions.
1047 // If boundary markers are malformed, slicing self-heals by falling back to
1048 // full history instead of crashing or dropping all diffs.
1049 await this.preparePendingStateFromMessages(messages);
1050
1051 const nextCompactionEpoch = getNextCompactionEpoch(messages);
1052 assert(Number.isInteger(nextCompactionEpoch), "next compaction epoch must be an integer");
1053
1054 const previousBoundaryHistorySequence = getLatestBoundaryHistorySequence(messages);
1055 const maxExistingHistorySequence = this.getMaxExistingHistorySequence(messages);
1056
1057 // For idle compaction, preserve the original recency timestamp so the workspace
1058 // doesn't appear "recently used" in the sidebar. Use the shared recency utility
1059 // to ensure consistency with how the sidebar computes recency.
1060 let timestamp = Date.now();
1061 if (isIdleCompaction) {
1062 const recency = computeRecencyFromMessages(messages);
1063 if (recency !== null) {
1064 timestamp = recency;
1065 }
1066 }

Callers 1

handleCompletionMethod · 0.95

Calls 15

emitChatEventMethod · 0.95
createMuxMessageFunction · 0.90
ErrFunction · 0.90
isNonNegativeIntegerFunction · 0.90
OkFunction · 0.90

Tested by

no test coverage detected