@@ -2,7 +2,7 @@ import type { Logger } from "../logger"
22import type { SessionStats , GCStats } from "../core/janitor"
33import type { ToolMetadata } from "../fetch-wrapper/types"
44import { formatTokenCount } from "../tokenizer"
5- import { extractParameterKey } from "./display-utils"
5+ import { formatPrunedItemsList } from "./display-utils"
66
77export type PruningSummaryLevel = "off" | "minimal" | "detailed"
88
@@ -26,6 +26,31 @@ export interface NotificationData {
2626 sessionStats : SessionStats | null
2727}
2828
29+ export async function sendUnifiedNotification (
30+ ctx : NotificationContext ,
31+ sessionID : string ,
32+ data : NotificationData ,
33+ agent ?: string
34+ ) : Promise < boolean > {
35+ const hasAiPruning = data . aiPrunedCount > 0
36+ const hasGcActivity = data . gcPending && data . gcPending . toolsDeduped > 0
37+
38+ if ( ! hasAiPruning && ! hasGcActivity ) {
39+ return false
40+ }
41+
42+ if ( ctx . config . pruningSummary === 'off' ) {
43+ return false
44+ }
45+
46+ const message = ctx . config . pruningSummary === 'minimal'
47+ ? buildMinimalMessage ( data )
48+ : buildDetailedMessage ( data , ctx . config . workingDirectory )
49+
50+ await sendIgnoredMessage ( ctx , sessionID , message , agent )
51+ return true
52+ }
53+
2954export async function sendIgnoredMessage (
3055 ctx : NotificationContext ,
3156 sessionID : string ,
@@ -50,35 +75,25 @@ export async function sendIgnoredMessage(
5075 }
5176}
5277
53- export async function sendUnifiedNotification (
54- ctx : NotificationContext ,
55- sessionID : string ,
56- data : NotificationData ,
57- agent ?: string
58- ) : Promise < boolean > {
59- const hasAiPruning = data . aiPrunedCount > 0
60- const hasGcActivity = data . gcPending && data . gcPending . toolsDeduped > 0
61-
62- if ( ! hasAiPruning && ! hasGcActivity ) {
63- return false
64- }
78+ function buildMinimalMessage ( data : NotificationData ) : string {
79+ const { justNowTokens, totalTokens } = calculateStats ( data )
80+ return formatStatsHeader ( totalTokens , justNowTokens )
81+ }
6582
66- if ( ctx . config . pruningSummary === 'off' ) {
67- return false
68- }
83+ function buildDetailedMessage ( data : NotificationData , workingDirectory ?: string ) : string {
84+ const { justNowTokens, totalTokens } = calculateStats ( data )
6985
70- const message = ctx . config . pruningSummary === 'minimal'
71- ? buildMinimalMessage ( data )
72- : buildDetailedMessage ( data , ctx . config . workingDirectory )
86+ let message = formatStatsHeader ( totalTokens , justNowTokens )
7387
74- await sendIgnoredMessage ( ctx , sessionID , message , agent )
75- return true
76- }
88+ if ( data . aiPrunedCount > 0 ) {
89+ const justNowTokensStr = `~ ${ formatTokenCount ( justNowTokens ) } `
90+ message += `\n\n▣ Pruned tools ( ${ justNowTokensStr } )`
7791
78- function buildMinimalMessage ( data : NotificationData ) : string {
79- const { justNowTokens, totalTokens } = calculateStats ( data )
92+ const itemLines = formatPrunedItemsList ( data . aiPrunedIds , data . toolMetadata , workingDirectory )
93+ message += '\n' + itemLines . join ( '\n' )
94+ }
8095
81- return formatStatsHeader ( totalTokens , justNowTokens )
96+ return message . trim ( )
8297}
8398
8499function calculateStats ( data : NotificationData ) : {
@@ -108,94 +123,3 @@ function formatStatsHeader(
108123 `▣ DCP | ${ totalTokensPadded } saved total` ,
109124 ] . join ( '\n' )
110125}
111-
112- function buildDetailedMessage ( data : NotificationData , workingDirectory ?: string ) : string {
113- const { justNowTokens, totalTokens } = calculateStats ( data )
114-
115- let message = formatStatsHeader ( totalTokens , justNowTokens )
116-
117- if ( data . aiPrunedCount > 0 ) {
118- const justNowTokensStr = `~${ formatTokenCount ( justNowTokens ) } `
119- message += `\n\n▣ Pruned tools (${ justNowTokensStr } )`
120-
121- for ( const prunedId of data . aiPrunedIds ) {
122- const normalizedId = prunedId . toLowerCase ( )
123- const metadata = data . toolMetadata . get ( normalizedId )
124-
125- if ( metadata ) {
126- const paramKey = extractParameterKey ( metadata )
127- if ( paramKey ) {
128- const displayKey = truncate ( shortenPath ( paramKey , workingDirectory ) , 60 )
129- message += `\n→ ${ metadata . tool } : ${ displayKey } `
130- } else {
131- message += `\n→ ${ metadata . tool } `
132- }
133- }
134- }
135-
136- const knownCount = data . aiPrunedIds . filter ( id =>
137- data . toolMetadata . has ( id . toLowerCase ( ) )
138- ) . length
139- const unknownCount = data . aiPrunedIds . length - knownCount
140-
141- if ( unknownCount > 0 ) {
142- message += `\n→ (${ unknownCount } tool${ unknownCount > 1 ? 's' : '' } with unknown metadata)`
143- }
144- }
145-
146- return message . trim ( )
147- }
148-
149- function truncate ( str : string , maxLen : number = 60 ) : string {
150- if ( str . length <= maxLen ) return str
151- return str . slice ( 0 , maxLen - 3 ) + '...'
152- }
153-
154- function shortenPath ( input : string , workingDirectory ?: string ) : string {
155- const inPathMatch = input . match ( / ^ ( .+ ) i n ( .+ ) $ / )
156- if ( inPathMatch ) {
157- const prefix = inPathMatch [ 1 ]
158- const pathPart = inPathMatch [ 2 ]
159- const shortenedPath = shortenSinglePath ( pathPart , workingDirectory )
160- return `${ prefix } in ${ shortenedPath } `
161- }
162-
163- return shortenSinglePath ( input , workingDirectory )
164- }
165-
166- function shortenSinglePath ( path : string , workingDirectory ?: string ) : string {
167- const homeDir = require ( 'os' ) . homedir ( )
168-
169- if ( workingDirectory ) {
170- if ( path . startsWith ( workingDirectory + '/' ) ) {
171- return path . slice ( workingDirectory . length + 1 )
172- }
173- if ( path === workingDirectory ) {
174- return '.'
175- }
176- }
177-
178- if ( path . startsWith ( homeDir ) ) {
179- path = '~' + path . slice ( homeDir . length )
180- }
181-
182- const nodeModulesMatch = path . match ( / n o d e _ m o d u l e s \/ ( @ [ ^ \/ ] + \/ [ ^ \/ ] + | [ ^ \/ ] + ) \/ ( .* ) / )
183- if ( nodeModulesMatch ) {
184- return `${ nodeModulesMatch [ 1 ] } /${ nodeModulesMatch [ 2 ] } `
185- }
186-
187- if ( workingDirectory ) {
188- const workingDirWithTilde = workingDirectory . startsWith ( homeDir )
189- ? '~' + workingDirectory . slice ( homeDir . length )
190- : null
191-
192- if ( workingDirWithTilde && path . startsWith ( workingDirWithTilde + '/' ) ) {
193- return path . slice ( workingDirWithTilde . length + 1 )
194- }
195- if ( workingDirWithTilde && path === workingDirWithTilde ) {
196- return '.'
197- }
198- }
199-
200- return path
201- }
0 commit comments