interfaceCompactionEntry<T=unknown>{type:"compaction";id: string;parentId: string;timestamp: number;summary: string;firstKeptEntryId: string;tokensBefore: number;usage?: Usage;// LLM usage that generated the summary
fromHook?: boolean;// true if provided by extension (legacy field name)
details?: T;// implementation-specific data
}// Default compaction uses this for details (from compaction.ts):
interfaceCompactionDetails{readFiles: string[];modifiedFiles: string[];}
Tree before navigation:
┌─ B ─ C ─ D (old leaf, being abandoned)
A ───┤
└─ E ─ F (target)
Common ancestor: A
Entries to summarize: B, C, D
After navigation with summary:
┌─ B ─ C ─ D
A ───┤
└─ E ─ F ─ [summary of B,C,D] (new leaf)
interfaceBranchSummaryEntry<T=unknown>{type:"branch_summary";id: string;parentId: string;timestamp: number;summary: string;fromId: string;// Entry we navigated from
usage?: Usage;// LLM usage that generated the summary
fromHook?: boolean;// true if provided by extension (legacy field name)
details?: T;// implementation-specific data
}// Default branch summarization uses this for details (from branch-summarization.ts):
interfaceBranchSummaryDetails{readFiles: string[];modifiedFiles: string[];}
[User]: What they said
[Assistant thinking]: Internal reasoning
[Assistant]: Response text
[Assistant tool calls]: read(path="foo.ts"); edit(path="bar.ts", ...)
[Tool result]: Output from tool
pi.on("session_before_compact",async(event,ctx)=>{const{preparation,branchEntries,customInstructions,reason,willRetry,signal}=event;// preparation.messagesToSummarize - messages to summarize
// preparation.turnPrefixMessages - split turn prefix (if isSplitTurn)
// preparation.previousSummary - previous compaction summary
// preparation.fileOps - extracted file operations
// preparation.tokensBefore - context tokens before compaction
// preparation.firstKeptEntryId - where kept messages start
// preparation.settings - effective settings after applying model overrides
// branchEntries - all entries on current branch (for custom state)
// reason - "manual" (/compact), "threshold", or "overflow"
// willRetry - whether the aborted turn is retried after compaction (overflow recovery)
// signal - AbortSignal (pass to LLM calls)
// Cancel:
return{cancel: true};// Custom summary:
return{compaction:{summary:"Your summary...",firstKeptEntryId: preparation.firstKeptEntryId,tokensBefore: preparation.tokensBefore,// usage: summaryResponse.usage, // Optional; included in session totals
details:{/* custom data */},}};});
将消息转换为文本
要用你自己的模型生成摘要,请使用 serializeConversation 将消息转换为文本:
import{convertToLlm,serializeConversation}from"@earendil-works/pi-coding-agent";pi.on("session_before_compact",async(event,ctx)=>{const{preparation}=event;// Convert AgentMessage[] to Message[], then serialize to text
constconversationText=serializeConversation(convertToLlm(preparation.messagesToSummarize));// Returns:
// [User]: message text
// [Assistant thinking]: thinking content
// [Assistant]: response text
// [Assistant tool calls]: read(path="..."); bash(command="...")
// [Tool result]: output text
// Now send to your model for summarization
const{summary,usage}=awaitmyModel.summarize(conversationText);return{compaction:{summary,firstKeptEntryId: preparation.firstKeptEntryId,tokensBefore: preparation.tokensBefore,usage,}};});
pi.on("session_compact_failed",async(event,ctx)=>{const{reason,errorMessage,aborted,willRetry,fromExtension}=event;// reason - "manual" (/compact), "threshold", or "overflow"
// errorMessage - present for non-abort failures
// aborted - true for cancelled/aborted compactions
// willRetry - whether the aborted turn would have retried after compaction
// fromExtension - whether extension-provided compaction content was being used
});
session_before_tree
在 /tree 导航之前触发。无论用户是否选择摘要都会触发。可以取消导航或提供自定义摘要。
pi.on("session_before_tree",async(event,ctx)=>{const{preparation,signal}=event;// preparation.targetId - where we're navigating to
// preparation.oldLeafId - current position (being abandoned)
// preparation.commonAncestorId - shared ancestor
// preparation.entriesToSummarize - entries that would be summarized
// preparation.userWantsSummary - whether user chose to summarize
// Cancel navigation entirely:
return{cancel: true};// Provide custom summary (only used if userWantsSummary is true):
if(preparation.userWantsSummary){return{summary:{summary:"Your summary...",// usage: summaryResponse.usage, // Optional; included in session totals
details:{/* custom data */},}};}});